| /* |
| * Copyright (C) 2015 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.car.pm; |
| |
| import android.annotation.Nullable; |
| import android.app.ActivityManager.StackInfo; |
| import android.car.Car; |
| import android.car.content.pm.AppBlockingPackageInfo; |
| import android.car.content.pm.CarAppBlockingPolicy; |
| import android.car.content.pm.CarAppBlockingPolicyService; |
| import android.car.content.pm.CarPackageManager; |
| import android.car.content.pm.ICarPackageManager; |
| import android.car.drivingstate.CarUxRestrictions; |
| import android.car.drivingstate.ICarUxRestrictionsChangeListener; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.PackageInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.content.pm.ResolveInfo; |
| import android.content.pm.ServiceInfo; |
| import android.content.pm.Signature; |
| import android.content.res.Resources; |
| import android.os.Binder; |
| import android.os.Build; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.Process; |
| import android.text.TextUtils; |
| import android.text.format.DateFormat; |
| import android.util.ArraySet; |
| import android.util.Log; |
| import android.util.Pair; |
| |
| import com.android.car.CarLog; |
| import com.android.car.CarServiceBase; |
| import com.android.car.CarServiceUtils; |
| import com.android.car.CarUxRestrictionsManagerService; |
| import com.android.car.R; |
| import com.android.car.SystemActivityMonitoringService; |
| import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer; |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| public class CarPackageManagerService extends ICarPackageManager.Stub implements CarServiceBase { |
| private static final boolean DBG_POLICY_SET = false; |
| private static final boolean DBG_POLICY_CHECK = false; |
| private static final boolean DBG_POLICY_ENFORCEMENT = false; |
| // Delimiters to parse packages and activities in the configuration XML resource. |
| private static final String PACKAGE_DELIMITER = ","; |
| private static final String PACKAGE_ACTIVITY_DELIMITER = "/"; |
| private static final int LOG_SIZE = 20; |
| |
| private final Context mContext; |
| private final SystemActivityMonitoringService mSystemActivityMonitoringService; |
| private final PackageManager mPackageManager; |
| |
| private final HandlerThread mHandlerThread; |
| private final PackageHandler mHandler; |
| |
| // For dumpsys logging. |
| private final LinkedList<String> mBlockedActivityLogs = new LinkedList<>(); |
| |
| // Store the white list and black list strings from the resource file. |
| private String mConfiguredWhitelist; |
| private String mConfiguredBlacklist; |
| private final List<String> mAllowedAppInstallSources; |
| |
| /** |
| * Hold policy set from policy service or client. |
| * Key: packageName of policy service |
| */ |
| @GuardedBy("this") |
| private final HashMap<String, ClientPolicy> mClientPolicies = new HashMap<>(); |
| @GuardedBy("this") |
| private HashMap<String, AppBlockingPackageInfoWrapper> mActivityWhitelistMap = new HashMap<>(); |
| // The list corresponding to the one configured in <activityBlacklist> |
| @GuardedBy("this") |
| private HashMap<String, AppBlockingPackageInfoWrapper> mActivityBlacklistMap = new HashMap<>(); |
| @GuardedBy("this") |
| private LinkedList<AppBlockingPolicyProxy> mProxies; |
| |
| @GuardedBy("this") |
| private final LinkedList<CarAppBlockingPolicy> mWaitingPolicies = new LinkedList<>(); |
| |
| private final CarUxRestrictionsManagerService mCarUxRestrictionsService; |
| private boolean mEnableActivityBlocking; |
| private final ComponentName mActivityBlockingActivity; |
| |
| private final ActivityLaunchListener mActivityLaunchListener = new ActivityLaunchListener(); |
| private final UxRestrictionsListener mUxRestrictionsListener; |
| |
| // Information related to when the installed packages should be parsed for building a white and |
| // black list |
| private final List<String> mPackageManagerActions = Arrays.asList( |
| Intent.ACTION_PACKAGE_ADDED, |
| Intent.ACTION_PACKAGE_CHANGED, |
| Intent.ACTION_PACKAGE_DATA_CLEARED, |
| Intent.ACTION_PACKAGE_REMOVED, |
| Intent.ACTION_PACKAGE_REPLACED, |
| Intent.ACTION_PACKAGE_FULLY_REMOVED); |
| |
| private final PackageParsingEventReceiver mPackageParsingEventReceiver = |
| new PackageParsingEventReceiver(); |
| private final BootEventReceiver mBootEventReceiver = new BootEventReceiver(); |
| |
| // To track if the packages have been parsed for building white/black lists. If we haven't had |
| // received any intents (boot complete or package changed), then the white list is null leading |
| // to blocking everything. So, no blocking until we have had a chance to parse the packages. |
| private boolean mHasParsedPackages; |
| // To track if we received the boot complete intent. |
| private boolean mBootLockedIntentRx; |
| |
| public CarPackageManagerService(Context context, |
| CarUxRestrictionsManagerService uxRestrictionsService, |
| SystemActivityMonitoringService systemActivityMonitoringService) { |
| mContext = context; |
| mCarUxRestrictionsService = uxRestrictionsService; |
| mSystemActivityMonitoringService = systemActivityMonitoringService; |
| mPackageManager = mContext.getPackageManager(); |
| mUxRestrictionsListener = new UxRestrictionsListener(uxRestrictionsService); |
| mHandlerThread = new HandlerThread(CarLog.TAG_PACKAGE); |
| mHandlerThread.start(); |
| mHandler = new PackageHandler(mHandlerThread.getLooper()); |
| Resources res = context.getResources(); |
| mEnableActivityBlocking = res.getBoolean(R.bool.enableActivityBlockingForSafety); |
| String blockingActivity = res.getString(R.string.activityBlockingActivity); |
| mActivityBlockingActivity = ComponentName.unflattenFromString(blockingActivity); |
| mAllowedAppInstallSources = Arrays.asList( |
| res.getStringArray(R.array.allowedAppInstallSources)); |
| } |
| |
| |
| @Override |
| public void setAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy, int flags) { |
| if (DBG_POLICY_SET) { |
| Log.i(CarLog.TAG_PACKAGE, "policy setting from binder call, client:" + packageName); |
| } |
| doSetAppBlockingPolicy(packageName, policy, flags, true /*setNow*/); |
| } |
| |
| /** |
| * Restarts the requested task. If task with {@code taskId} does not exist, do nothing. |
| */ |
| @Override |
| public void restartTask(int taskId) { |
| mSystemActivityMonitoringService.restartTask(taskId); |
| } |
| |
| private void doSetAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy, int flags, |
| boolean setNow) { |
| if (mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_APP_BLOCKING) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException( |
| "requires permission " + Car.PERMISSION_CONTROL_APP_BLOCKING); |
| } |
| CarServiceUtils.assertPackageName(mContext, packageName); |
| if (policy == null) { |
| throw new IllegalArgumentException("policy cannot be null"); |
| } |
| if ((flags & CarPackageManager.FLAG_SET_POLICY_ADD) != 0 && |
| (flags & CarPackageManager.FLAG_SET_POLICY_REMOVE) != 0) { |
| throw new IllegalArgumentException( |
| "Cannot set both FLAG_SET_POLICY_ADD and FLAG_SET_POLICY_REMOVE flag"); |
| } |
| mHandler.requestUpdatingPolicy(packageName, policy, flags); |
| if (setNow) { |
| mHandler.requestPolicySetting(); |
| if ((flags & CarPackageManager.FLAG_SET_POLICY_WAIT_FOR_CHANGE) != 0) { |
| synchronized (policy) { |
| try { |
| policy.wait(); |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public boolean isActivityDistractionOptimized(String packageName, String className) { |
| assertPackageAndClassName(packageName, className); |
| synchronized (this) { |
| if (DBG_POLICY_CHECK) { |
| Log.i(CarLog.TAG_PACKAGE, "isActivityDistractionOptimized" |
| + dumpPoliciesLocked(false)); |
| } |
| AppBlockingPackageInfo info = searchFromBlacklistsLocked(packageName); |
| if (info != null) { |
| return false; |
| } |
| return isActivityInWhitelistsLocked(packageName, className); |
| } |
| } |
| |
| @Override |
| public boolean isServiceDistractionOptimized(String packageName, String className) { |
| if (packageName == null) { |
| throw new IllegalArgumentException("Package name null"); |
| } |
| synchronized (this) { |
| if (DBG_POLICY_CHECK) { |
| Log.i(CarLog.TAG_PACKAGE, "isServiceDistractionOptimized" |
| + dumpPoliciesLocked(false)); |
| } |
| AppBlockingPackageInfo info = searchFromBlacklistsLocked(packageName); |
| if (info != null) { |
| return false; |
| } |
| info = searchFromWhitelistsLocked(packageName); |
| if (info != null) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isActivityBackedBySafeActivity(ComponentName activityName) { |
| if (!mUxRestrictionsListener.isRestricted()) { |
| return true; |
| } |
| StackInfo info = mSystemActivityMonitoringService.getFocusedStackForTopActivity( |
| activityName); |
| if (info == null) { // not top in focused stack |
| return true; |
| } |
| if (info.taskNames.length <= 1) { // nothing below this. |
| return false; |
| } |
| ComponentName activityBehind = ComponentName.unflattenFromString( |
| info.taskNames[info.taskNames.length - 2]); |
| return isActivityDistractionOptimized(activityBehind.getPackageName(), |
| activityBehind.getClassName()); |
| } |
| |
| public Looper getLooper() { |
| return mHandlerThread.getLooper(); |
| } |
| |
| private void assertPackageAndClassName(String packageName, String className) { |
| if (packageName == null) { |
| throw new IllegalArgumentException("Package name null"); |
| } |
| if (className == null) { |
| throw new IllegalArgumentException("Class name null"); |
| } |
| } |
| |
| @GuardedBy("this") |
| private AppBlockingPackageInfo searchFromBlacklistsLocked(String packageName) { |
| for (ClientPolicy policy : mClientPolicies.values()) { |
| AppBlockingPackageInfoWrapper wrapper = policy.blacklistsMap.get(packageName); |
| if (wrapper != null && wrapper.isMatching) { |
| return wrapper.info; |
| } |
| } |
| AppBlockingPackageInfoWrapper wrapper = mActivityBlacklistMap.get(packageName); |
| return (wrapper != null) ? wrapper.info : null; |
| } |
| |
| @GuardedBy("this") |
| private AppBlockingPackageInfo searchFromWhitelistsLocked(String packageName) { |
| for (ClientPolicy policy : mClientPolicies.values()) { |
| AppBlockingPackageInfoWrapper wrapper = policy.whitelistsMap.get(packageName); |
| if (wrapper != null && wrapper.isMatching) { |
| return wrapper.info; |
| } |
| } |
| AppBlockingPackageInfoWrapper wrapper = mActivityWhitelistMap.get(packageName); |
| return (wrapper != null) ? wrapper.info : null; |
| } |
| |
| @GuardedBy("this") |
| private boolean isActivityInWhitelistsLocked(String packageName, String className) { |
| for (ClientPolicy policy : mClientPolicies.values()) { |
| if (isActivityInMapAndMatching(policy.whitelistsMap, packageName, className)) { |
| return true; |
| } |
| } |
| return isActivityInMapAndMatching(mActivityWhitelistMap, packageName, className); |
| } |
| |
| private boolean isActivityInMapAndMatching(HashMap<String, AppBlockingPackageInfoWrapper> map, |
| String packageName, String className) { |
| AppBlockingPackageInfoWrapper wrapper = map.get(packageName); |
| if (wrapper == null || !wrapper.isMatching) { |
| if (DBG_POLICY_CHECK) { |
| Log.d(CarLog.TAG_PACKAGE, "Pkg not in whitelist:" + packageName); |
| } |
| return false; |
| } |
| return wrapper.info.isActivityCovered(className); |
| } |
| |
| @Override |
| public void init() { |
| synchronized (this) { |
| mHandler.requestInit(); |
| } |
| } |
| |
| @Override |
| public void release() { |
| synchronized (this) { |
| mHandler.requestRelease(); |
| // wait for release do be done. This guarantees that init is done. |
| try { |
| wait(); |
| } catch (InterruptedException e) { |
| } |
| mHasParsedPackages = false; |
| mActivityWhitelistMap.clear(); |
| mActivityBlacklistMap.clear(); |
| mClientPolicies.clear(); |
| if (mProxies != null) { |
| for (AppBlockingPolicyProxy proxy : mProxies) { |
| proxy.disconnect(); |
| } |
| mProxies.clear(); |
| } |
| wakeupClientsWaitingForPolicySetitngLocked(); |
| } |
| mContext.unregisterReceiver(mPackageParsingEventReceiver); |
| mContext.unregisterReceiver(mBootEventReceiver); |
| mCarUxRestrictionsService.unregisterUxRestrictionsChangeListener(mUxRestrictionsListener); |
| mSystemActivityMonitoringService.registerActivityLaunchListener(null); |
| } |
| |
| // run from HandlerThread |
| private void doHandleInit() { |
| startAppBlockingPolicies(); |
| IntentFilter bootIntent = new IntentFilter(); |
| bootIntent.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED); |
| mContext.registerReceiver(mBootEventReceiver, bootIntent); |
| IntentFilter pkgParseIntent = new IntentFilter(); |
| for (String action : mPackageManagerActions) { |
| pkgParseIntent.addAction(action); |
| } |
| pkgParseIntent.addDataScheme("package"); |
| mContext.registerReceiver(mPackageParsingEventReceiver, pkgParseIntent); |
| try { |
| mCarUxRestrictionsService.registerUxRestrictionsChangeListener(mUxRestrictionsListener); |
| } catch (IllegalArgumentException e) { |
| // can happen while mocking is going on while init is still done. |
| Log.w(CarLog.TAG_PACKAGE, "sensor subscription failed", e); |
| return; |
| } |
| mSystemActivityMonitoringService.registerActivityLaunchListener( |
| mActivityLaunchListener); |
| } |
| |
| private void doParseInstalledPackages() { |
| generateActivityWhitelistMap(); |
| generateActivityBlacklistMap(); |
| synchronized (this) { |
| mHasParsedPackages = true; |
| } |
| mUxRestrictionsListener.checkIfTopActivityNeedsBlocking(); |
| } |
| |
| private synchronized void doHandleRelease() { |
| notifyAll(); |
| } |
| |
| @GuardedBy("this") |
| private void wakeupClientsWaitingForPolicySetitngLocked() { |
| for (CarAppBlockingPolicy waitingPolicy : mWaitingPolicies) { |
| synchronized (waitingPolicy) { |
| waitingPolicy.notifyAll(); |
| } |
| } |
| mWaitingPolicies.clear(); |
| } |
| |
| private void doSetPolicy() { |
| synchronized (this) { |
| wakeupClientsWaitingForPolicySetitngLocked(); |
| } |
| blockTopActivitiesIfNecessary(); |
| } |
| |
| private void doUpdatePolicy(String packageName, CarAppBlockingPolicy policy, int flags) { |
| if (DBG_POLICY_SET) { |
| Log.i(CarLog.TAG_PACKAGE, "setting policy from:" + packageName + ",policy:" + policy + |
| ",flags:0x" + Integer.toHexString(flags)); |
| } |
| AppBlockingPackageInfoWrapper[] blacklistWrapper = verifyList(policy.blacklists); |
| AppBlockingPackageInfoWrapper[] whitelistWrapper = verifyList(policy.whitelists); |
| synchronized (this) { |
| ClientPolicy clientPolicy = mClientPolicies.get(packageName); |
| if (clientPolicy == null) { |
| clientPolicy = new ClientPolicy(); |
| mClientPolicies.put(packageName, clientPolicy); |
| } |
| if ((flags & CarPackageManager.FLAG_SET_POLICY_ADD) != 0) { |
| clientPolicy.addToBlacklists(blacklistWrapper); |
| clientPolicy.addToWhitelists(whitelistWrapper); |
| } else if ((flags & CarPackageManager.FLAG_SET_POLICY_REMOVE) != 0) { |
| clientPolicy.removeBlacklists(blacklistWrapper); |
| clientPolicy.removeWhitelists(whitelistWrapper); |
| } else { //replace. |
| clientPolicy.replaceBlacklists(blacklistWrapper); |
| clientPolicy.replaceWhitelists(whitelistWrapper); |
| } |
| if ((flags & CarPackageManager.FLAG_SET_POLICY_WAIT_FOR_CHANGE) != 0) { |
| mWaitingPolicies.add(policy); |
| } |
| if (DBG_POLICY_SET) { |
| Log.i(CarLog.TAG_PACKAGE, "policy set:" + dumpPoliciesLocked(false)); |
| } |
| } |
| blockTopActivitiesIfNecessary(); |
| } |
| |
| private AppBlockingPackageInfoWrapper[] verifyList(AppBlockingPackageInfo[] list) { |
| if (list == null) { |
| return null; |
| } |
| LinkedList<AppBlockingPackageInfoWrapper> wrappers = new LinkedList<>(); |
| for (int i = 0; i < list.length; i++) { |
| AppBlockingPackageInfo info = list[i]; |
| if (info == null) { |
| continue; |
| } |
| boolean isMatching = isInstalledPackageMatching(info); |
| wrappers.add(new AppBlockingPackageInfoWrapper(info, isMatching)); |
| } |
| return wrappers.toArray(new AppBlockingPackageInfoWrapper[wrappers.size()]); |
| } |
| |
| boolean isInstalledPackageMatching(AppBlockingPackageInfo info) { |
| PackageInfo packageInfo = null; |
| try { |
| packageInfo = mPackageManager.getPackageInfo(info.packageName, |
| PackageManager.GET_SIGNATURES); |
| } catch (NameNotFoundException e) { |
| return false; |
| } |
| if (packageInfo == null) { |
| return false; |
| } |
| // if it is system app and client specified the flag, do not check signature |
| if ((info.flags & AppBlockingPackageInfo.FLAG_SYSTEM_APP) == 0 || |
| (!packageInfo.applicationInfo.isSystemApp() && |
| !packageInfo.applicationInfo.isUpdatedSystemApp())) { |
| Signature[] signatires = packageInfo.signatures; |
| if (!isAnySignatureMatching(signatires, info.signatures)) { |
| return false; |
| } |
| } |
| int version = packageInfo.versionCode; |
| if (info.minRevisionCode == 0) { |
| if (info.maxRevisionCode == 0) { // all versions |
| return true; |
| } else { // only max version matters |
| return info.maxRevisionCode > version; |
| } |
| } else { // min version matters |
| if (info.maxRevisionCode == 0) { |
| return info.minRevisionCode < version; |
| } else { |
| return (info.minRevisionCode < version) && (info.maxRevisionCode > version); |
| } |
| } |
| } |
| |
| /** |
| * Any signature from policy matching with package's signatures is treated as matching. |
| */ |
| boolean isAnySignatureMatching(Signature[] fromPackage, Signature[] fromPolicy) { |
| if (fromPackage == null) { |
| return false; |
| } |
| if (fromPolicy == null) { |
| return false; |
| } |
| ArraySet<Signature> setFromPackage = new ArraySet<Signature>(); |
| for (Signature sig : fromPackage) { |
| setFromPackage.add(sig); |
| } |
| for (Signature sig : fromPolicy) { |
| if (setFromPackage.contains(sig)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Generate a map of whitelisted packages and activities of the form {pkgName, Whitelisted |
| * activities}. The whitelist information can come from a configuration XML resource or from |
| * the apps marking their activities as distraction optimized. |
| */ |
| private void generateActivityWhitelistMap() { |
| HashMap<String, AppBlockingPackageInfoWrapper> activityWhitelist = new HashMap<>(); |
| mConfiguredWhitelist = mContext.getString(R.string.activityWhitelist); |
| if (mConfiguredWhitelist == null) { |
| if (DBG_POLICY_CHECK) { |
| Log.d(CarLog.TAG_PACKAGE, "Null whitelist in config"); |
| } |
| return; |
| } |
| // Get the apps/activities that are whitelisted in the configuration XML resource |
| HashMap<String, Set<String>> configWhitelist = parseConfiglist(mConfiguredWhitelist); |
| if (configWhitelist == null) { |
| if (DBG_POLICY_CHECK) { |
| Log.w(CarLog.TAG_PACKAGE, "White list null. No apps whitelisted"); |
| } |
| return; |
| } |
| // Add the blocking overlay activity to the whitelist, since that needs to run in a |
| // restricted state to communicate the reason an app was blocked. |
| Set<String> defaultActivity = new ArraySet<>(); |
| if (mActivityBlockingActivity != null) { |
| defaultActivity.add(mActivityBlockingActivity.getClassName()); |
| configWhitelist.put(mActivityBlockingActivity.getPackageName(), defaultActivity); |
| } |
| |
| List<PackageInfo> packages = mPackageManager.getInstalledPackages( |
| PackageManager.GET_SIGNATURES | PackageManager.GET_ACTIVITIES |
| | PackageManager.MATCH_DIRECT_BOOT_AWARE |
| | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); |
| |
| for (PackageInfo info : packages) { |
| if (info.applicationInfo == null) { |
| continue; |
| } |
| |
| int flags = 0; |
| String[] activities = null; |
| |
| if (info.applicationInfo.isSystemApp() |
| || info.applicationInfo.isUpdatedSystemApp()) { |
| flags = AppBlockingPackageInfo.FLAG_SYSTEM_APP; |
| } |
| |
| /* 1. Check if all or some of this app is in the <activityWhitelist> |
| in config.xml */ |
| Set<String> configActivitiesForPackage = configWhitelist.get(info.packageName); |
| if (configActivitiesForPackage != null) { |
| if (DBG_POLICY_CHECK) { |
| Log.d(CarLog.TAG_PACKAGE, info.packageName + " whitelisted"); |
| } |
| if (configActivitiesForPackage.size() == 0) { |
| // Whole Pkg has been whitelisted |
| flags |= AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY; |
| // Add all activities to the whitelist |
| activities = getActivitiesInPackage(info); |
| if (activities == null && DBG_POLICY_CHECK) { |
| Log.d(CarLog.TAG_PACKAGE, info.packageName + ": Activities null"); |
| } |
| } else { |
| if (DBG_POLICY_CHECK) { |
| Log.d(CarLog.TAG_PACKAGE, "Partially Whitelisted. WL Activities:"); |
| for (String a : configActivitiesForPackage) { |
| Log.d(CarLog.TAG_PACKAGE, a); |
| } |
| } |
| activities = configActivitiesForPackage.toArray( |
| new String[configActivitiesForPackage.size()]); |
| } |
| } else { |
| /* 2. If app is not listed in the config.xml check their Manifest meta-data to |
| see if they have any Distraction Optimized(DO) activities. |
| For non system apps, we check if the app install source was a permittable |
| source. This prevents side-loaded apps to fake DO. Bypass the check |
| for debug builds for development convenience. */ |
| if (!isDebugBuild() |
| && !info.applicationInfo.isSystemApp() |
| && !info.applicationInfo.isUpdatedSystemApp()) { |
| try { |
| if (mAllowedAppInstallSources != null) { |
| String installerName = mPackageManager.getInstallerPackageName( |
| info.packageName); |
| if (installerName == null || (installerName != null |
| && !mAllowedAppInstallSources.contains(installerName))) { |
| Log.w(CarLog.TAG_PACKAGE, |
| info.packageName + " not installed from permitted sources " |
| + installerName == null ? "NULL" : installerName); |
| continue; |
| } |
| } |
| } catch (IllegalArgumentException e) { |
| Log.w(CarLog.TAG_PACKAGE, info.packageName + " not installed!"); |
| continue; |
| } |
| } |
| |
| try { |
| activities = CarAppMetadataReader.findDistractionOptimizedActivities( |
| mContext, |
| info.packageName); |
| } catch (NameNotFoundException e) { |
| Log.w(CarLog.TAG_PACKAGE, "Error reading metadata: " + info.packageName); |
| continue; |
| } |
| if (activities != null) { |
| // Some of the activities in this app are Distraction Optimized. |
| if (DBG_POLICY_CHECK) { |
| for (String activity : activities) { |
| Log.d(CarLog.TAG_PACKAGE, |
| "adding " + activity + " from " + info.packageName |
| + " to whitelist"); |
| } |
| } |
| } |
| } |
| // Nothing to add to whitelist |
| if (activities == null) { |
| continue; |
| } |
| |
| Signature[] signatures; |
| signatures = info.signatures; |
| AppBlockingPackageInfo appBlockingInfo = new AppBlockingPackageInfo( |
| info.packageName, 0, 0, flags, signatures, activities); |
| AppBlockingPackageInfoWrapper wrapper = new AppBlockingPackageInfoWrapper( |
| appBlockingInfo, true); |
| activityWhitelist.put(info.packageName, wrapper); |
| } |
| synchronized (this) { |
| mActivityWhitelistMap.clear(); |
| mActivityWhitelistMap.putAll(activityWhitelist); |
| } |
| } |
| |
| private boolean isDebugBuild() { |
| return Build.IS_USERDEBUG || Build.IS_ENG; |
| } |
| |
| /** |
| * Generate a map of blacklisted packages and activities of the form {pkgName, Blacklisted |
| * activities}. The blacklist information comes from a configuration XML resource. |
| */ |
| private void generateActivityBlacklistMap() { |
| HashMap<String, AppBlockingPackageInfoWrapper> activityBlacklist = new HashMap<>(); |
| mConfiguredBlacklist = mContext.getString(R.string.activityBlacklist); |
| if (mConfiguredBlacklist == null) { |
| if (DBG_POLICY_CHECK) { |
| Log.d(CarLog.TAG_PACKAGE, "Null blacklist in config"); |
| } |
| return; |
| } |
| Map<String, Set<String>> configBlacklist = parseConfiglist(mConfiguredBlacklist); |
| if (configBlacklist == null) { |
| if (DBG_POLICY_CHECK) { |
| Log.w(CarLog.TAG_PACKAGE, "Black list null. No apps blacklisted"); |
| } |
| return; |
| } |
| |
| for (String pkg : configBlacklist.keySet()) { |
| if (TextUtils.isEmpty(pkg)) { |
| // This means there is nothing to blacklist |
| Log.d(CarLog.TAG_PACKAGE, "Empty string in blacklist pkg"); |
| continue; |
| } |
| int flags = 0; |
| PackageInfo pkgInfo; |
| String[] activities; |
| try { |
| pkgInfo = mPackageManager.getPackageInfo( |
| pkg, PackageManager.GET_ACTIVITIES |
| | PackageManager.GET_SIGNING_CERTIFICATES |
| | PackageManager.MATCH_DIRECT_BOOT_AWARE |
| | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); |
| } catch (NameNotFoundException e) { |
| Log.e(CarLog.TAG_PACKAGE, pkg + " not found to blacklist ", e); |
| continue; |
| } |
| |
| if (pkgInfo.applicationInfo.isSystemApp() |
| || pkgInfo.applicationInfo.isUpdatedSystemApp()) { |
| flags |= AppBlockingPackageInfo.FLAG_SYSTEM_APP; |
| } |
| Set<String> configActivities = configBlacklist.get(pkg); |
| if (configActivities.size() == 0) { |
| // whole package |
| flags |= AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY; |
| activities = getActivitiesInPackage(pkgInfo); |
| } else { |
| activities = configActivities.toArray(new String[configActivities.size()]); |
| } |
| |
| if (activities == null) { |
| continue; |
| } |
| AppBlockingPackageInfo appBlockingInfo = new AppBlockingPackageInfo(pkg, 0, 0, flags, |
| pkgInfo.signatures, activities); |
| AppBlockingPackageInfoWrapper wrapper = new AppBlockingPackageInfoWrapper( |
| appBlockingInfo, true); |
| activityBlacklist.put(pkg, wrapper); |
| } |
| synchronized (this) { |
| mActivityBlacklistMap.clear(); |
| mActivityBlacklistMap.putAll(activityBlacklist); |
| } |
| } |
| |
| /** |
| * Parses the given resource and returns a map of packages and activities. |
| * Key is package name and value is list of activities. Empty list implies whole package is |
| * included. |
| */ |
| @Nullable |
| private HashMap<String, Set<String>> parseConfiglist(String configList) { |
| if (configList == null) { |
| return null; |
| } |
| HashMap<String, Set<String>> packageToActivityMap = new HashMap<>(); |
| String[] entries = configList.split(PACKAGE_DELIMITER); |
| for (String entry : entries) { |
| String[] packageActivityPair = entry.split(PACKAGE_ACTIVITY_DELIMITER); |
| Set<String> activities = packageToActivityMap.get(packageActivityPair[0]); |
| boolean newPackage = false; |
| if (activities == null) { |
| activities = new ArraySet<>(); |
| newPackage = true; |
| packageToActivityMap.put(packageActivityPair[0], activities); |
| } |
| if (packageActivityPair.length == 1) { // whole package |
| activities.clear(); |
| } else if (packageActivityPair.length == 2) { |
| // add class name only when the whole package is not whitelisted. |
| if (newPackage || (activities.size() > 0)) { |
| activities.add(packageActivityPair[1]); |
| } |
| } |
| } |
| return packageToActivityMap; |
| } |
| |
| @Nullable |
| private String[] getActivitiesInPackage(PackageInfo info) { |
| if (info == null || info.activities == null) { |
| return null; |
| } |
| List<String> activityList = new ArrayList<>(); |
| for (ActivityInfo aInfo : info.activities) { |
| activityList.add(aInfo.name); |
| } |
| return activityList.toArray(new String[activityList.size()]); |
| } |
| |
| /** |
| * Checks if there are any {@link CarAppBlockingPolicyService} and creates a proxy to |
| * bind to them and retrieve the {@link CarAppBlockingPolicy} |
| */ |
| @VisibleForTesting |
| public void startAppBlockingPolicies() { |
| Intent policyIntent = new Intent(); |
| policyIntent.setAction(CarAppBlockingPolicyService.SERVICE_INTERFACE); |
| List<ResolveInfo> policyInfos = mPackageManager.queryIntentServices(policyIntent, 0); |
| if (policyInfos == null) { //no need to wait for service binding and retrieval. |
| mHandler.requestPolicySetting(); |
| return; |
| } |
| LinkedList<AppBlockingPolicyProxy> proxies = new LinkedList<>(); |
| for (ResolveInfo resolveInfo : policyInfos) { |
| ServiceInfo serviceInfo = resolveInfo.serviceInfo; |
| if (serviceInfo == null) { |
| continue; |
| } |
| if (serviceInfo.isEnabled()) { |
| if (mPackageManager.checkPermission(Car.PERMISSION_CONTROL_APP_BLOCKING, |
| serviceInfo.packageName) != PackageManager.PERMISSION_GRANTED) { |
| continue; |
| } |
| Log.i(CarLog.TAG_PACKAGE, "found policy holding service:" + serviceInfo); |
| AppBlockingPolicyProxy proxy = new AppBlockingPolicyProxy(this, mContext, |
| serviceInfo); |
| proxy.connect(); |
| proxies.add(proxy); |
| } |
| } |
| synchronized (this) { |
| mProxies = proxies; |
| } |
| } |
| |
| public void onPolicyConnectionAndSet(AppBlockingPolicyProxy proxy, |
| CarAppBlockingPolicy policy) { |
| doHandlePolicyConnection(proxy, policy); |
| } |
| |
| public void onPolicyConnectionFailure(AppBlockingPolicyProxy proxy) { |
| doHandlePolicyConnection(proxy, null); |
| } |
| |
| private void doHandlePolicyConnection(AppBlockingPolicyProxy proxy, |
| CarAppBlockingPolicy policy) { |
| boolean shouldSetPolicy = false; |
| synchronized (this) { |
| if (mProxies == null) { |
| proxy.disconnect(); |
| return; |
| } |
| mProxies.remove(proxy); |
| if (mProxies.size() == 0) { |
| shouldSetPolicy = true; |
| mProxies = null; |
| } |
| } |
| try { |
| if (policy != null) { |
| if (DBG_POLICY_SET) { |
| Log.i(CarLog.TAG_PACKAGE, "policy setting from policy service:" + |
| proxy.getPackageName()); |
| } |
| doSetAppBlockingPolicy(proxy.getPackageName(), policy, 0, false /*setNow*/); |
| } |
| } finally { |
| proxy.disconnect(); |
| if (shouldSetPolicy) { |
| mHandler.requestPolicySetting(); |
| } |
| } |
| } |
| |
| @Override |
| public void dump(PrintWriter writer) { |
| synchronized (this) { |
| writer.println("*PackageManagementService*"); |
| writer.println("mEnableActivityBlocking:" + mEnableActivityBlocking); |
| writer.println("mHasParsedPackages:" + mHasParsedPackages); |
| writer.println("mBootLockedIntentRx:" + mBootLockedIntentRx); |
| writer.println("ActivityRestricted:" + mUxRestrictionsListener.isRestricted()); |
| writer.println(String.join("\n", mBlockedActivityLogs)); |
| writer.print(dumpPoliciesLocked(true)); |
| } |
| } |
| |
| @GuardedBy("this") |
| private String dumpPoliciesLocked(boolean dumpAll) { |
| StringBuilder sb = new StringBuilder(); |
| if (dumpAll) { |
| sb.append("**System white list**\n"); |
| for (AppBlockingPackageInfoWrapper wrapper : mActivityWhitelistMap.values()) { |
| sb.append(wrapper.toString() + "\n"); |
| } |
| sb.append("**System Black list**\n"); |
| for (AppBlockingPackageInfoWrapper wrapper : mActivityBlacklistMap.values()) { |
| sb.append(wrapper.toString() + "\n"); |
| } |
| } |
| sb.append("**Client Policies**\n"); |
| for (Entry<String, ClientPolicy> entry : mClientPolicies.entrySet()) { |
| sb.append("Client:" + entry.getKey() + "\n"); |
| sb.append(" whitelists:\n"); |
| for (AppBlockingPackageInfoWrapper wrapper : entry.getValue().whitelistsMap.values()) { |
| sb.append(wrapper.toString() + "\n"); |
| } |
| sb.append(" blacklists:\n"); |
| for (AppBlockingPackageInfoWrapper wrapper : entry.getValue().blacklistsMap.values()) { |
| sb.append(wrapper.toString() + "\n"); |
| } |
| } |
| sb.append("**Unprocessed policy services**\n"); |
| if (mProxies != null) { |
| for (AppBlockingPolicyProxy proxy : mProxies) { |
| sb.append(proxy.toString() + "\n"); |
| } |
| } |
| sb.append("**Whitelist string in resource**\n"); |
| sb.append(mConfiguredWhitelist + "\n"); |
| |
| sb.append("**Blacklist string in resource**\n"); |
| sb.append(mConfiguredBlacklist + "\n"); |
| |
| return sb.toString(); |
| } |
| |
| private void blockTopActivityIfNecessary(TopTaskInfoContainer topTask) { |
| boolean restricted = mUxRestrictionsListener.isRestricted(); |
| if (!restricted) { |
| return; |
| } |
| doBlockTopActivityIfNotAllowed(topTask); |
| } |
| |
| private void doBlockTopActivityIfNotAllowed(TopTaskInfoContainer topTask) { |
| if (topTask.topActivity == null) { |
| return; |
| } |
| boolean allowed = isActivityDistractionOptimized( |
| topTask.topActivity.getPackageName(), |
| topTask.topActivity.getClassName()); |
| if (DBG_POLICY_ENFORCEMENT) { |
| Log.i(CarLog.TAG_PACKAGE, "new activity:" + topTask.toString() + " allowed:" + allowed); |
| } |
| if (allowed) { |
| return; |
| } |
| synchronized (this) { |
| if (!mEnableActivityBlocking) { |
| Log.d(CarLog.TAG_PACKAGE, "Current activity " + topTask.topActivity + |
| " not allowed, blocking disabled. Number of tasks in stack:" |
| + topTask.stackInfo.taskIds.length); |
| return; |
| } |
| } |
| if (DBG_POLICY_CHECK) { |
| Log.i(CarLog.TAG_PACKAGE, "Current activity " + topTask.topActivity + |
| " not allowed, will block, number of tasks in stack:" + |
| topTask.stackInfo.taskIds.length); |
| } |
| StringBuilder blockedActivityLog = new StringBuilder(); |
| Intent newActivityIntent = new Intent(); |
| newActivityIntent.setComponent(mActivityBlockingActivity); |
| newActivityIntent.putExtra( |
| ActivityBlockingActivity.INTENT_KEY_BLOCKED_ACTIVITY, |
| topTask.topActivity.flattenToString()); |
| blockedActivityLog.append("Blocked activity ") |
| .append(topTask.topActivity.flattenToShortString()) |
| .append(". Task id ").append(topTask.taskId); |
| |
| // If root activity of blocked task is DO, also pass its task id into blocking activity, |
| // which uses the id to display a button for restarting the blocked task. |
| for (int i = 0; i < topTask.stackInfo.taskIds.length; i++) { |
| // topTask.taskId is the task that should be blocked. |
| if (topTask.stackInfo.taskIds[i] == topTask.taskId) { |
| // stackInfo represents an ActivityStack. Its fields taskIds and taskNames |
| // are 1:1 mapped, where taskNames is the name of root activity in this task. |
| String taskRootActivity = topTask.stackInfo.taskNames[i]; |
| |
| ComponentName rootActivityName = ComponentName.unflattenFromString( |
| taskRootActivity); |
| if (isActivityDistractionOptimized( |
| rootActivityName.getPackageName(), rootActivityName.getClassName())) { |
| newActivityIntent.putExtra( |
| ActivityBlockingActivity.EXTRA_BLOCKED_TASK, topTask.taskId); |
| if (Log.isLoggable(CarLog.TAG_PACKAGE, Log.INFO)) { |
| Log.i(CarLog.TAG_PACKAGE, "Blocked task " + topTask.taskId |
| + " has DO root activity " + taskRootActivity); |
| } |
| blockedActivityLog.append(". Root DO activity ") |
| .append(rootActivityName.flattenToShortString()); |
| } |
| break; |
| } |
| } |
| addLog(blockedActivityLog.toString()); |
| mSystemActivityMonitoringService.blockActivity(topTask, newActivityIntent); |
| } |
| |
| private void blockTopActivitiesIfNecessary() { |
| boolean restricted = mUxRestrictionsListener.isRestricted(); |
| if (!restricted) { |
| return; |
| } |
| List<TopTaskInfoContainer> topTasks = mSystemActivityMonitoringService.getTopTasks(); |
| for (TopTaskInfoContainer topTask : topTasks) { |
| doBlockTopActivityIfNotAllowed(topTask); |
| } |
| } |
| |
| @Override |
| public synchronized void setEnableActivityBlocking(boolean enable) { |
| // Check if the caller has the same signature as that of the car service. |
| if (mPackageManager.checkSignatures(Process.myUid(), Binder.getCallingUid()) |
| != PackageManager.SIGNATURE_MATCH) { |
| throw new SecurityException( |
| "Caller " + mPackageManager.getNameForUid(Binder.getCallingUid()) |
| + " does not have the right signature"); |
| } |
| mEnableActivityBlocking = enable; |
| } |
| |
| /** |
| * Get the distraction optimized activities for the given package. |
| * |
| * @param pkgName Name of the package |
| * @return Array of the distraction optimized activities in the package |
| */ |
| @Nullable |
| public String[] getDistractionOptimizedActivities(String pkgName) { |
| try { |
| return CarAppMetadataReader.findDistractionOptimizedActivities(mContext, pkgName); |
| } catch (NameNotFoundException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Append one line of log for dumpsys. |
| * |
| * <p>Maintains the size of log by {@link #LOG_SIZE} and appends tag and timestamp to the line. |
| */ |
| private void addLog(String log) { |
| while (mBlockedActivityLogs.size() >= LOG_SIZE) { |
| mBlockedActivityLogs.remove(); |
| } |
| StringBuffer sb = new StringBuffer() |
| .append(CarLog.TAG_PACKAGE).append(':') |
| .append(DateFormat.format( |
| "MM-dd HH:mm:ss", System.currentTimeMillis())).append(": ") |
| .append(log); |
| mBlockedActivityLogs.add(sb.toString()); |
| } |
| |
| /** |
| * Reading policy and setting policy can take time. Run it in a separate handler thread. |
| */ |
| private class PackageHandler extends Handler { |
| private final int MSG_INIT = 0; |
| private final int MSG_PARSE_PKG = 1; |
| private final int MSG_SET_POLICY = 2; |
| private final int MSG_UPDATE_POLICY = 3; |
| private final int MSG_RELEASE = 4; |
| |
| private PackageHandler(Looper looper) { |
| super(looper); |
| } |
| |
| private void requestInit() { |
| Message msg = obtainMessage(MSG_INIT); |
| sendMessage(msg); |
| } |
| |
| private void requestRelease() { |
| removeMessages(MSG_INIT); |
| removeMessages(MSG_SET_POLICY); |
| removeMessages(MSG_UPDATE_POLICY); |
| Message msg = obtainMessage(MSG_RELEASE); |
| sendMessage(msg); |
| } |
| |
| private void requestPolicySetting() { |
| Message msg = obtainMessage(MSG_SET_POLICY); |
| sendMessage(msg); |
| } |
| |
| private void requestUpdatingPolicy(String packageName, CarAppBlockingPolicy policy, |
| int flags) { |
| Pair<String, CarAppBlockingPolicy> pair = new Pair<>(packageName, policy); |
| Message msg = obtainMessage(MSG_UPDATE_POLICY, flags, 0, pair); |
| sendMessage(msg); |
| } |
| |
| private void requestParsingInstalledPkgs(long delayMs) { |
| Message msg = obtainMessage(MSG_PARSE_PKG); |
| if (delayMs == 0) { |
| sendMessage(msg); |
| } else { |
| sendMessageDelayed(msg, delayMs); |
| } |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case MSG_INIT: |
| doHandleInit(); |
| break; |
| case MSG_PARSE_PKG: |
| removeMessages(MSG_PARSE_PKG); |
| doParseInstalledPackages(); |
| break; |
| case MSG_SET_POLICY: |
| doSetPolicy(); |
| break; |
| case MSG_UPDATE_POLICY: |
| Pair<String, CarAppBlockingPolicy> pair = |
| (Pair<String, CarAppBlockingPolicy>) msg.obj; |
| doUpdatePolicy(pair.first, pair.second, msg.arg1); |
| break; |
| case MSG_RELEASE: |
| doHandleRelease(); |
| break; |
| } |
| } |
| } |
| |
| private static class AppBlockingPackageInfoWrapper { |
| private final AppBlockingPackageInfo info; |
| /** |
| * Whether the current info is matching with the target package in system. Mismatch can |
| * happen for version out of range or signature mismatch. |
| */ |
| private boolean isMatching; |
| |
| private AppBlockingPackageInfoWrapper(AppBlockingPackageInfo info, boolean isMatching) { |
| this.info = info; |
| this.isMatching = isMatching; |
| } |
| |
| @Override |
| public String toString() { |
| return "AppBlockingPackageInfoWrapper [info=" + info + ", isMatching=" + isMatching + |
| "]"; |
| } |
| } |
| |
| /** |
| * Client policy holder per each client. Should be accessed with CarpackageManagerService.this |
| * held. |
| */ |
| private static class ClientPolicy { |
| private final HashMap<String, AppBlockingPackageInfoWrapper> whitelistsMap = |
| new HashMap<>(); |
| private final HashMap<String, AppBlockingPackageInfoWrapper> blacklistsMap = |
| new HashMap<>(); |
| |
| private void replaceWhitelists(AppBlockingPackageInfoWrapper[] whitelists) { |
| whitelistsMap.clear(); |
| addToWhitelists(whitelists); |
| } |
| |
| private void addToWhitelists(AppBlockingPackageInfoWrapper[] whitelists) { |
| if (whitelists == null) { |
| return; |
| } |
| for (AppBlockingPackageInfoWrapper wrapper : whitelists) { |
| if (wrapper != null) { |
| whitelistsMap.put(wrapper.info.packageName, wrapper); |
| } |
| } |
| } |
| |
| private void removeWhitelists(AppBlockingPackageInfoWrapper[] whitelists) { |
| if (whitelists == null) { |
| return; |
| } |
| for (AppBlockingPackageInfoWrapper wrapper : whitelists) { |
| if (wrapper != null) { |
| whitelistsMap.remove(wrapper.info.packageName); |
| } |
| } |
| } |
| |
| private void replaceBlacklists(AppBlockingPackageInfoWrapper[] blacklists) { |
| blacklistsMap.clear(); |
| addToBlacklists(blacklists); |
| } |
| |
| private void addToBlacklists(AppBlockingPackageInfoWrapper[] blacklists) { |
| if (blacklists == null) { |
| return; |
| } |
| for (AppBlockingPackageInfoWrapper wrapper : blacklists) { |
| if (wrapper != null) { |
| blacklistsMap.put(wrapper.info.packageName, wrapper); |
| } |
| } |
| } |
| |
| private void removeBlacklists(AppBlockingPackageInfoWrapper[] blacklists) { |
| if (blacklists == null) { |
| return; |
| } |
| for (AppBlockingPackageInfoWrapper wrapper : blacklists) { |
| if (wrapper != null) { |
| blacklistsMap.remove(wrapper.info.packageName); |
| } |
| } |
| } |
| } |
| |
| private class ActivityLaunchListener |
| implements SystemActivityMonitoringService.ActivityLaunchListener { |
| @Override |
| public void onActivityLaunch(TopTaskInfoContainer topTask) { |
| blockTopActivityIfNecessary(topTask); |
| } |
| } |
| |
| /** |
| * Listens to the UX restrictions from {@link CarUxRestrictionsManagerService} and initiates |
| * checking if the foreground Activity should be blocked. |
| */ |
| private class UxRestrictionsListener extends ICarUxRestrictionsChangeListener.Stub { |
| @GuardedBy("this") |
| @Nullable |
| private CarUxRestrictions mCurrentUxRestrictions; |
| private final CarUxRestrictionsManagerService uxRestrictionsService; |
| |
| public UxRestrictionsListener(CarUxRestrictionsManagerService service) { |
| uxRestrictionsService = service; |
| mCurrentUxRestrictions = uxRestrictionsService.getCurrentUxRestrictions(); |
| } |
| |
| @Override |
| public void onUxRestrictionsChanged(CarUxRestrictions restrictions) { |
| if (DBG_POLICY_CHECK) { |
| Log.d(CarLog.TAG_PACKAGE, "Received uxr restrictions: " |
| + restrictions.isRequiresDistractionOptimization() |
| + " : " + restrictions.getActiveRestrictions()); |
| } |
| // We are not handling the restrictions until we know what is allowed and what is not. |
| // This is to handle some situations, where car service is ready and getting sensor |
| // data but we haven't received the boot complete intents. |
| if (!mHasParsedPackages) { |
| return; |
| } |
| |
| synchronized (this) { |
| mCurrentUxRestrictions = new CarUxRestrictions(restrictions); |
| } |
| checkIfTopActivityNeedsBlocking(); |
| } |
| |
| private void checkIfTopActivityNeedsBlocking() { |
| boolean shouldCheck = false; |
| synchronized (this) { |
| if (mCurrentUxRestrictions != null |
| && mCurrentUxRestrictions.isRequiresDistractionOptimization()) { |
| shouldCheck = true; |
| } |
| } |
| if (DBG_POLICY_CHECK) { |
| Log.d(CarLog.TAG_PACKAGE, "block?: " + shouldCheck); |
| } |
| if (shouldCheck) { |
| blockTopActivitiesIfNecessary(); |
| } |
| } |
| |
| private synchronized boolean isRestricted() { |
| // if current restrictions is null, try querying the service, once. |
| if (mCurrentUxRestrictions == null) { |
| mCurrentUxRestrictions = uxRestrictionsService.getCurrentUxRestrictions(); |
| } |
| if (mCurrentUxRestrictions != null) { |
| return mCurrentUxRestrictions.isRequiresDistractionOptimization(); |
| } |
| // If restriction information is still not available (could happen during bootup), |
| // return not restricted. This maintains parity with previous implementation but needs |
| // a revisit as we test more. |
| return false; |
| } |
| } |
| |
| /** |
| * Listens to the Boot intent to initiate parsing installed packages. |
| */ |
| private class BootEventReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (intent == null || intent.getAction() == null) { |
| return; |
| } |
| if (DBG_POLICY_CHECK) { |
| Log.d(CarLog.TAG_PACKAGE, "BootEventReceiver Received " + intent.getAction()); |
| } |
| if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction())) { |
| mHandler.requestParsingInstalledPkgs(0); |
| mBootLockedIntentRx = true; |
| } |
| } |
| } |
| |
| /** |
| * Listens to the package install/uninstall events to know when to initiate parsing |
| * installed packages. |
| */ |
| private class PackageParsingEventReceiver extends BroadcastReceiver { |
| private static final long PACKAGE_PARSING_DELAY_MS = 500; |
| |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (intent == null || intent.getAction() == null) { |
| return; |
| } |
| if (DBG_POLICY_CHECK) { |
| Log.d(CarLog.TAG_PACKAGE, |
| "PackageParsingEventReceiver Received " + intent.getAction()); |
| } |
| String action = intent.getAction(); |
| if (isPackageManagerAction(action)) { |
| // send a delayed message so if we received multiple related intents, we parse |
| // only once. |
| logEventChange(intent); |
| mHandler.requestParsingInstalledPkgs(PACKAGE_PARSING_DELAY_MS); |
| } |
| } |
| |
| private boolean isPackageManagerAction(String action) { |
| return mPackageManagerActions.indexOf(action) != -1; |
| } |
| |
| /** |
| * Convenience log function to log what changed. Logs only when more debug logs |
| * are needed - DBG_POLICY_CHECK needs to be true |
| */ |
| private void logEventChange(Intent intent) { |
| if (!DBG_POLICY_CHECK || intent == null) { |
| return; |
| } |
| |
| String packageName = intent.getData().getSchemeSpecificPart(); |
| Log.d(CarLog.TAG_PACKAGE, "Pkg Changed:" + packageName); |
| String action = intent.getAction(); |
| if (action == null) { |
| return; |
| } |
| if (action.equals(Intent.ACTION_PACKAGE_CHANGED)) { |
| Log.d(CarLog.TAG_PACKAGE, "Changed components"); |
| String[] cc = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); |
| if (cc != null) { |
| for (String c : cc) { |
| Log.d(CarLog.TAG_PACKAGE, c); |
| } |
| } |
| } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) |
| || action.equals(Intent.ACTION_PACKAGE_ADDED)) { |
| Log.d(CarLog.TAG_PACKAGE, action + " Replacing?: " + intent.getBooleanExtra( |
| Intent.EXTRA_REPLACING, false)); |
| } |
| } |
| } |
| } |