Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c39b0b23 authored by Tony Huang's avatar Tony Huang
Browse files

Overlay manifest setting by package manager

Using overlay manager to get package name of overlay apk.
And parse it's resources to get config value directly.

Register PRE_BOOT_COMPLETED to set up the value.

Bug: 134558563
Test: Preload RRO with "is_launcher_enabled:false", flash all device,
      boot to verify if DocumentsUI launcher icon hidden.
Test: Preload RRO with "is_launcher_enabled:false", factory reset,
      and verify if DocumentsUI launcher icon hidden.
Change-Id: Ifc5f90c853a9a2b8b367fa53a678a9ea959e441a
parent 28d5f8ee
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -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. -->
+1 −0
Original line number Diff line number Diff line
@@ -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 -->
+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);
        }
    }
}
+32 −3
Original line number Diff line number Diff line
@@ -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()