Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Mobile Device Manager feature with 3 keys. #8698

Merged
merged 4 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog.d/8698.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Add support for Mobile Device Management.
The keys are:
- default homeserver URL `im.vector.app.serverConfigDefaultHomeserverUrlString`
- push gateway URL `im.vector.app.serverConfigSygnalAPIUrlString`
- permalink base URL `im.vector.app.clientPermalinkBaseUrl`
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import im.vector.app.core.pushers.PushParser
import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.pushers.UnifiedPushHelper
import im.vector.app.core.pushers.VectorPushHandler
import im.vector.app.features.mdm.MdmData
import im.vector.app.features.mdm.MdmService
import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
Expand All @@ -46,13 +48,15 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
@Inject lateinit var pushParser: PushParser
@Inject lateinit var vectorPushHandler: VectorPushHandler
@Inject lateinit var unifiedPushHelper: UnifiedPushHelper
@Inject lateinit var mdmService: MdmService

private val scope = CoroutineScope(SupervisorJob())

override fun onDestroy() {
scope.cancel()
super.onDestroy()
}

override fun onNewToken(token: String) {
Timber.tag(loggerTag.value).d("New Firebase token")
fcmHelper.storeFcmToken(token)
Expand All @@ -62,7 +66,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
unifiedPushHelper.isEmbeddedDistributor()
) {
scope.launch {
pushersManager.enqueueRegisterPusher(token, getString(R.string.pusher_http_url))
pushersManager.enqueueRegisterPusher(
pushKey = token,
gateway = mdmService.getData(
mdmData = MdmData.DefaultPushGatewayUrl,
defaultValue = getString(R.string.pusher_http_url),
),
)
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions vector-app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
tools:ignore="UnusedAttribute"
tools:replace="android:allowBackup">

<meta-data
android:name="android.content.APP_RESTRICTIONS"
android:resource="@xml/vector_app_restrictions" />

<!-- Activity alias for the launcher Activity (must be declared after the Activity it targets) -->
<!-- exported="true" is required to launch application -->
<activity-alias
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ import im.vector.app.features.analytics.metrics.VectorPlugins
import im.vector.app.features.configuration.VectorCustomEventTypesProvider
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.invite.CompileTimeAutoAcceptInvites
import im.vector.app.features.mdm.DefaultMdmService
import im.vector.app.features.mdm.MdmData
import im.vector.app.features.mdm.MdmService
import im.vector.app.features.navigation.DefaultNavigator
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.pin.PinCodeStore
Expand Down Expand Up @@ -108,6 +111,9 @@ import javax.inject.Singleton
@Binds
abstract fun bindEmojiSpanify(emojiCompatWrapper: EmojiCompatWrapper): EmojiSpanify

@Binds
abstract fun bindMdmService(service: DefaultMdmService): MdmService

@Binds
abstract fun bindFontScale(fontScale: FontScalePreferencesImpl): FontScalePreferences

Expand Down Expand Up @@ -145,6 +151,7 @@ import javax.inject.Singleton
flipperProxy: FlipperProxy,
vectorPlugins: VectorPlugins,
vectorCustomEventTypesProvider: VectorCustomEventTypesProvider,
mdmService: MdmService,
): MatrixConfiguration {
return MatrixConfiguration(
applicationFlavor = BuildConfig.FLAVOR_DESCRIPTION,
Expand All @@ -156,6 +163,7 @@ import javax.inject.Singleton
metricPlugins = vectorPlugins.plugins(),
cryptoAnalyticsPlugin = vectorPlugins.cryptoMetricPlugin,
customEventTypesProvider = vectorCustomEventTypesProvider,
clientPermalinkBaseUrl = mdmService.getData(MdmData.PermalinkBaseUrl),
syncConfig = SyncConfig(
syncFilterParams = SyncFilterParams(lazyLoadMembersForStateEvents = true, useThreadNotifications = true)
)
Expand Down
4 changes: 4 additions & 0 deletions vector-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- The https://github.com/LikeTheSalad/android-stem requires a non empty strings.xml -->
<string name="ignored_placeholder" tools:ignore="UnusedResources">ignored</string>

<string name="restriction_default_homeserver_url_title">Default homeserver URL</string>
<string name="restriction_default_push_gateway_title">Default Push gateway</string>
<string name="restriction_permalink_base_url_title">Permalink base url</string>
</resources>
18 changes: 18 additions & 0 deletions vector-app/src/main/res/xml/vector_app_restrictions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
<restriction
android:key="im.vector.app.serverConfigDefaultHomeserverUrlString"
android:restrictionType="string"
android:title="@string/restriction_default_homeserver_url_title" />

<restriction
android:key="im.vector.app.serverConfigSygnalAPIUrlString"
android:restrictionType="string"
android:title="@string/restriction_default_push_gateway_title" />

<restriction
android:key="im.vector.app.clientPermalinkBaseUrl"
android:restrictionType="string"
android:title="@string/restriction_permalink_base_url_title" />

</restrictions>
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.configuration.VectorConfiguration
import im.vector.app.features.consent.ConsentNotGivenHelper
import im.vector.app.features.mdm.MdmService
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.pin.PinLocker
import im.vector.app.features.pin.PinMode
Expand Down Expand Up @@ -171,6 +172,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var errorFormatter: ErrorFormatter
@Inject lateinit var mdmService: MdmService

// For debug only
@Inject lateinit var debugReceiver: DebugReceiver
Expand Down Expand Up @@ -412,6 +414,10 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
rageShake.start()
}
debugReceiver.register(this)
mdmService.registerListener(this) {
// Just log that a change occurred.
Timber.w("MDM data has been updated")
}
}

private val postResumeScheduledActions = mutableListOf<() -> Unit>()
Expand Down Expand Up @@ -442,6 +448,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver

rageShake.stop()
debugReceiver.unregister(this)
mdmService.unregisterListener(this)
}

override fun onWindowFocusChanged(hasFocus: Boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.resources.AppNameProvider
import im.vector.app.core.resources.LocaleProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.mdm.MdmData
import im.vector.app.features.mdm.MdmService
import org.matrix.android.sdk.api.session.pushers.HttpPusher
import org.matrix.android.sdk.api.session.pushers.Pusher
import java.util.UUID
Expand All @@ -37,6 +39,7 @@ class PushersManager @Inject constructor(
private val stringProvider: StringProvider,
private val appNameProvider: AppNameProvider,
private val getDeviceInfoUseCase: GetDeviceInfoUseCase,
private val mdmService: MdmService,
) {
suspend fun testPush() {
val currentSession = activeSessionHolder.getActiveSession()
Expand All @@ -50,7 +53,10 @@ class PushersManager @Inject constructor(
}

suspend fun enqueueRegisterPusherWithFcmKey(pushKey: String): UUID {
return enqueueRegisterPusher(pushKey, stringProvider.getString(R.string.pusher_http_url))
return enqueueRegisterPusher(
pushKey = pushKey,
gateway = mdmService.getData(MdmData.DefaultPushGatewayUrl, stringProvider.getString(R.string.pusher_http_url))
)
}

suspend fun enqueueRegisterPusher(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import com.squareup.moshi.JsonClass
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.getApplicationLabel
import im.vector.app.features.mdm.MdmData
import im.vector.app.features.mdm.MdmService
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.cache.CacheStrategy
import org.matrix.android.sdk.api.failure.Failure
Expand All @@ -40,6 +42,7 @@ class UnifiedPushHelper @Inject constructor(
private val stringProvider: StringProvider,
private val matrix: Matrix,
private val fcmHelper: FcmHelper,
private val mdmService: MdmService,
) {

@MainThread
Expand Down Expand Up @@ -99,7 +102,12 @@ class UnifiedPushHelper @Inject constructor(
// register app_id type upfcm on sygnal
// the pushkey if FCM key
if (UnifiedPush.getDistributor(context) == context.packageName) {
unifiedPushStore.storePushGateway(stringProvider.getString(R.string.pusher_http_url))
unifiedPushStore.storePushGateway(
gateway = mdmService.getData(
mdmData = MdmData.DefaultPushGatewayUrl,
defaultValue = stringProvider.getString(R.string.pusher_http_url),
)
)
onDoneRunnable?.run()
return
}
Expand Down Expand Up @@ -185,7 +193,13 @@ class UnifiedPushHelper @Inject constructor(
}

fun getPushGateway(): String? {
return if (isEmbeddedDistributor()) stringProvider.getString(R.string.pusher_http_url)
else unifiedPushStore.getPushGateway()
return if (isEmbeddedDistributor()) {
mdmService.getData(
mdmData = MdmData.DefaultPushGatewayUrl,
defaultValue = stringProvider.getString(R.string.pusher_http_url),
)
} else {
unifiedPushStore.getPushGateway()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import javax.inject.Inject

/**
* Object to store and retrieve home and identity server urls.
* Note: this class is not used.
*/
class ServerUrlsRepository @Inject constructor(
@DefaultPreferences
Expand Down Expand Up @@ -89,5 +90,5 @@ class ServerUrlsRepository @Inject constructor(
/**
* Return default homeserver url from resources.
*/
fun getDefaultHomeServerUrl() = stringProvider.getString(R.string.matrix_org_server_url)
private fun getDefaultHomeServerUrl() = stringProvider.getString(R.string.matrix_org_server_url)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.app.features.mdm

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.RestrictionsManager
import androidx.core.content.getSystemService
import dagger.hilt.android.qualifiers.ApplicationContext
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class DefaultMdmService @Inject constructor(
@ApplicationContext applicationContext: Context
) : MdmService {
private val restrictionsManager = applicationContext.getSystemService<RestrictionsManager>()
private var onChangedListener: (() -> Unit)? = null

private val restrictionsReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Timber.w("Restrictions changed")
onChangedListener?.invoke()
}
}

override fun registerListener(context: Context, onChangedListener: () -> Unit) {
val restrictionsFilter = IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED)
this.onChangedListener = onChangedListener
context.registerReceiver(restrictionsReceiver, restrictionsFilter)
}

override fun unregisterListener(context: Context) {
context.unregisterReceiver(restrictionsReceiver)
this.onChangedListener = null
}

override fun getData(mdmData: MdmData): String? {
return restrictionsManager?.applicationRestrictions?.getString(mdmData.key)
}
}
39 changes: 39 additions & 0 deletions vector/src/main/java/im/vector/app/features/mdm/MdmService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.app.features.mdm

import android.content.Context
import timber.log.Timber

enum class MdmData(val key: String) {
DefaultHomeserverUrl(key = "im.vector.app.serverConfigDefaultHomeserverUrlString"),
DefaultPushGatewayUrl(key = "im.vector.app.serverConfigSygnalAPIUrlString"),
PermalinkBaseUrl(key = "im.vector.app.clientPermalinkBaseUrl"),
}

interface MdmService {
fun registerListener(context: Context, onChangedListener: () -> Unit)
fun unregisterListener(context: Context)
fun getData(mdmData: MdmData): String?
fun getData(mdmData: MdmData, defaultValue: String): String {
return getData(mdmData)
?.also {
Timber.w("Using MDM data for ${mdmData.name}: $it")
}
?: defaultValue
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import im.vector.app.features.login.LoginMode
import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.login.ServerType
import im.vector.app.features.login.SignMode
import im.vector.app.features.mdm.MdmData
import im.vector.app.features.mdm.MdmService
import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction
import im.vector.app.features.onboarding.StartAuthenticationFlowUseCase.StartAuthenticationResult
import kotlinx.coroutines.Job
Expand Down Expand Up @@ -93,6 +95,7 @@ class OnboardingViewModel @AssistedInject constructor(
private val registrationActionHandler: RegistrationActionHandler,
private val sdkIntProvider: BuildVersionSdkIntProvider,
private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase,
mdmService: MdmService,
) : VectorViewModel<OnboardingViewState, OnboardingAction, OnboardingViewEvents>(initialState) {

@AssistedFactory
Expand Down Expand Up @@ -143,7 +146,7 @@ class OnboardingViewModel @AssistedInject constructor(
}

private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
private val defaultHomeserverUrl = matrixOrgUrl
private val defaultHomeserverUrl = mdmService.getData(MdmData.DefaultHomeserverUrl, matrixOrgUrl)
giomfo marked this conversation as resolved.
Show resolved Hide resolved

private val registrationWizard: RegistrationWizard
get() = authenticationService.getRegistrationWizard()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package im.vector.app.core.pushers

import im.vector.app.R
import im.vector.app.features.mdm.NoOpMdmService
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeAppNameProvider
import im.vector.app.test.fakes.FakeGetDeviceInfoUseCase
Expand Down Expand Up @@ -54,6 +55,7 @@ class PushersManagerTest {
stringProvider.instance,
appNameProvider,
getDeviceInfoUseCase,
NoOpMdmService(),
)

@Test
Expand Down
Loading
Loading