Loading AndroidManifest.xml +7 −1 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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. --> Loading res/values/overlayable.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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 --> Loading src/com/android/documentsui/PreBootReceiver.java 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * 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); } } } src/com/android/documentsui/theme/ThemeOverlayManager.java +32 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -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() Loading Loading
AndroidManifest.xml +7 −1 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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. --> Loading
res/values/overlayable.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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 --> Loading
src/com/android/documentsui/PreBootReceiver.java 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * 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); } } }
src/com/android/documentsui/theme/ThemeOverlayManager.java +32 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -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() Loading