1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.activityembedding
18 
19 import android.app.Activity
20 import android.content.ActivityNotFoundException
21 import android.content.Context
22 import android.content.Intent
23 import android.content.pm.UserInfo
24 import android.provider.Settings
25 import android.util.Log
26 import com.android.settings.SettingsActivity
27 import com.android.settings.Utils
28 import com.android.settings.homepage.DeepLinkHomepageActivityInternal
29 import com.android.settings.homepage.SettingsHomepageActivity
30 import com.android.settings.password.PasswordUtils
31 import com.android.settingslib.spaprivileged.framework.common.userManager
32 
33 object EmbeddedDeepLinkUtils {
34     private const val TAG = "EmbeddedDeepLinkUtils"
35 
36     @JvmStatic
tryStartMultiPaneDeepLinknull37     fun Activity.tryStartMultiPaneDeepLink(
38         intent: Intent,
39         highlightMenuKey: String? = null,
40     ): Boolean {
41         intent.putExtra(
42             SettingsActivity.EXTRA_INITIAL_CALLING_PACKAGE,
43             PasswordUtils.getCallingAppPackageName(activityToken),
44         )
45         val trampolineIntent: Intent
46         if (intent.getBooleanExtra(SettingsActivity.EXTRA_IS_FROM_SLICE, false)) {
47             // Get menu key for slice deep link case.
48             var sliceHighlightMenuKey: String? = intent.getStringExtra(
49                 Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY
50             )
51             if (sliceHighlightMenuKey.isNullOrEmpty()) {
52                 sliceHighlightMenuKey = highlightMenuKey
53             }
54             trampolineIntent = getTrampolineIntent(intent, sliceHighlightMenuKey)
55             trampolineIntent.setClass(this, DeepLinkHomepageActivityInternal::class.java)
56         } else {
57             trampolineIntent = getTrampolineIntent(intent, highlightMenuKey)
58         }
59         return startTrampolineIntent(trampolineIntent)
60     }
61 
62     /**
63      * Returns the deep link trampoline intent for large screen devices.
64      */
65     @JvmStatic
getTrampolineIntentnull66     fun getTrampolineIntent(intent: Intent, highlightMenuKey: String?): Intent {
67         val detailIntent = Intent(intent)
68         // Guard against the arbitrary Intent injection.
69         if (detailIntent.selector != null) {
70             detailIntent.setSelector(null)
71         }
72         // It's a deep link intent, SettingsHomepageActivity will set SplitPairRule and start it.
73         return Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY).apply {
74             setPackage(Utils.SETTINGS_PACKAGE_NAME)
75             replaceExtras(detailIntent)
76 
77             // Relay detail intent data to prevent failure of Intent#ParseUri.
78             // If Intent#getData() is not null, Intent#toUri will return an Uri which has the scheme
79             // of Intent#getData() and it may not be the scheme of an Intent.
80             putExtra(
81                 SettingsHomepageActivity.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_DATA,
82                 detailIntent.data
83             )
84             detailIntent.setData(null)
85             putExtra(
86                 Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI,
87                 detailIntent.toUri(Intent.URI_INTENT_SCHEME)
88             )
89             putExtra(
90                 Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
91                 highlightMenuKey
92             )
93             addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
94         }
95     }
96 
97 
98     /**
99      * Returns whether the user is a sub profile.
100      */
101     @JvmStatic
isSubProfilenull102     fun isSubProfile(userInfo: UserInfo): Boolean =
103         userInfo.isManagedProfile || userInfo.isPrivateProfile
104 
105     private fun Context.startTrampolineIntent(trampolineIntent: Intent): Boolean = try {
106         val userInfo = userManager.getUserInfo(user.identifier)
107         if (isSubProfile(userInfo)) {
108             trampolineIntent.setClass(this, DeepLinkHomepageActivityInternal::class.java)
109                 .putExtra(SettingsActivity.EXTRA_USER_HANDLE, user)
110             startActivityAsUser(
111                 trampolineIntent,
112                 userManager.getProfileParent(userInfo.id).userHandle
113             )
114         } else {
115             startActivity(trampolineIntent)
116         }
117         true
118     } catch (e: ActivityNotFoundException) {
119         Log.e(TAG, "Deep link homepage is not available to show 2-pane UI")
120         false
121     }
122 }
123