diff options
-rw-r--r-- | AndroidManifest.xml | 8 | ||||
-rw-r--r-- | res/values/overlayable.xml | 1 | ||||
-rw-r--r-- | src/com/android/documentsui/PreBootReceiver.java | 104 | ||||
-rw-r--r-- | src/com/android/documentsui/theme/ThemeOverlayManager.java | 35 |
4 files changed, 144 insertions, 4 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ac16d214b..a2b01f33a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -116,7 +116,7 @@ </intent-filter> </activity> - <activity-alias android:name="ViewDownloadsActivity" + <activity-alias android:name=".ViewDownloadsActivity" android:targetActivity=".files.FilesActivity" android:enabled="@bool/handle_view_downloads_intent"> <intent-filter> @@ -169,6 +169,12 @@ </intent-filter> </receiver> + <receiver android:name=".PreBootReceiver"> + <intent-filter> + <action android:name="android.intent.action.PRE_BOOT_COMPLETED" /> + </intent-filter> + </receiver> + <!-- Run FileOperationService in a separate process so that we can use FileLock class to wait until jumbo clip is done writing to disk before reading it. See ClipStorage for details. --> diff --git a/res/values/overlayable.xml b/res/values/overlayable.xml index 23ca14e98..9a13207cf 100644 --- a/res/values/overlayable.xml +++ b/res/values/overlayable.xml @@ -45,6 +45,7 @@ <item type="bool" name="config_button_all_caps"/> <item type="bool" name="config_default_show_device_root"/> <item type="bool" name="feature_default_root_in_browse"/> + <item type="bool" name="handle_view_downloads_intent"/> <item type="bool" name="is_launcher_enabled"/> <item type="bool" name="show_search_bar"/> <!-- END BOOLEAN CONFIG --> diff --git a/src/com/android/documentsui/PreBootReceiver.java b/src/com/android/documentsui/PreBootReceiver.java new file mode 100644 index 000000000..a983adcca --- /dev/null +++ b/src/com/android/documentsui/PreBootReceiver.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * 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 com.android.documentsui; + +import static com.android.documentsui.base.SharedMinimal.DEBUG; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.om.OverlayInfo; +import android.content.om.OverlayManager; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.util.Log; + +import com.android.documentsui.theme.ThemeOverlayManager; + +/** + * A receiver listening action.PRE_BOOT_COMPLETED event for setting component enable or disable. + * Since there's limitation of overlay AndroidManifest.xml attrs at boot stage. + * The workaround to retrieve config from DocumentsUI RRO package at boot time in Q. + */ +public class PreBootReceiver extends BroadcastReceiver { + + private static final String TAG = "PreBootReceiver"; + private static final String CONFIG_IS_LAUNCHER_ENABLED = "is_launcher_enabled"; + private static final String CONFIG_HANDLE_VIEW_DOWNLOADS = "handle_view_downloads_intent"; + private static final String LAUNCHER_TARGET_CLASS = "com.android.documentsui.LauncherActivity"; + private static final String DOWNLOADS_TARGET_CLASS = + "com.android.documentsui.ViewDownloadsActivity"; + + @Override + public void onReceive(Context context, Intent intent) { + final PackageManager pm = context.getPackageManager(); + if (pm == null) { + Log.w(TAG, "Can't obtain PackageManager from System Service!"); + return; + } + + final OverlayManager om = context.getSystemService(OverlayManager.class); + if (om == null) { + Log.w(TAG, "Can't obtain OverlayManager from System Service!"); + return; + } + + final OverlayInfo info = new ThemeOverlayManager(om, + context.getPackageName()).getValidOverlay(pm); + + if (info == null) { + Log.w(TAG, "Can't get valid overlay info"); + return; + } + + final String overlayPkg = info.getPackageName(); + final String packageName = context.getPackageName(); + + Resources overlayRes; + try { + overlayRes = pm.getResourcesForApplication(overlayPkg); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Failed while parse package res."); + overlayRes = null; + } + if (overlayRes == null) { + return; + } + + setComponentEnabledByConfigResources(pm, packageName, LAUNCHER_TARGET_CLASS, + overlayPkg, overlayRes, CONFIG_IS_LAUNCHER_ENABLED); + setComponentEnabledByConfigResources(pm, packageName, DOWNLOADS_TARGET_CLASS, + overlayPkg, overlayRes, CONFIG_HANDLE_VIEW_DOWNLOADS); + } + + private static void setComponentEnabledByConfigResources(PackageManager pm, String packageName, + String className, String overlayPkg, Resources overlayRes, String config) { + int resId = overlayRes.getIdentifier(config, "bool", overlayPkg); + if (resId != 0) { + final ComponentName component = new ComponentName(packageName, className); + final boolean value = overlayRes.getBoolean(resId); + if (DEBUG) { + Log.i(TAG, "Overlay package:" + overlayPkg + ", customize " + config + ":" + value); + } + pm.setComponentEnabledSetting(component, value + ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED + : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + } + } +} diff --git a/src/com/android/documentsui/theme/ThemeOverlayManager.java b/src/com/android/documentsui/theme/ThemeOverlayManager.java index 623391810..ca8326870 100644 --- a/src/com/android/documentsui/theme/ThemeOverlayManager.java +++ b/src/com/android/documentsui/theme/ThemeOverlayManager.java @@ -19,13 +19,15 @@ package com.android.documentsui.theme; import android.content.Context; import android.content.om.OverlayInfo; import android.content.om.OverlayManager; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.AsyncTask; +import android.os.Environment; import android.os.UserHandle; -import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.core.util.Consumer; @@ -73,6 +75,34 @@ public class ThemeOverlayManager { return mOverlayManager.getOverlayInfosForTarget(mTargetPackageId, mUserHandle); } + /** + * Return the OverlayInfo which is provided by the docsUI overlay package located product, + * system or vendor. We assume there should only one docsUI overlay package because priority + * not work for non-static overlay, so vendor should put only one docsUI overlay package. + * + * @param pm the PackageManager + */ + @Nullable + public OverlayInfo getValidOverlay(@NonNull PackageManager pm) { + for (OverlayInfo info : getOverlayInfo()) { + try { + final ApplicationInfo ai = pm.getApplicationInfo(info.getPackageName(), 0); + // Since isProduct(), isVendor() and isSystemApp() functions in ApplicationInfo are + // hidden. The best way to avoid unknown sideload APKs is filter path by string + // comparison. + final String sourceDir = ai.sourceDir; + if (sourceDir.startsWith(Environment.getProductDirectory().getAbsolutePath()) + || sourceDir.startsWith(Environment.getVendorDirectory().getAbsolutePath()) + || sourceDir.startsWith(Environment.getRootDirectory().getAbsolutePath())) { + return info; + } + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Can't get ApplicationInfo of overlay package " + info.getPackageName()); + } + } + return null; + } + private void setEnabled(boolean enabled, Consumer<Boolean> callback) { new AsyncTask<Void, Void, Boolean>() { @Override @@ -94,8 +124,7 @@ public class ThemeOverlayManager { boolean bSuccess = true; for (OverlayInfo info : infos) { try { - if (info != null && !TextUtils.isEmpty(info.getPackageName()) - && info.isEnabled() != enabled) { + if (info.isEnabled() != enabled) { mOverlayManager.setEnabled(info.getPackageName(), enabled, mUserHandle); } else { Log.w(TAG, "Skip enabled overlay package:" + info.getPackageName() |