blob: 9f73b61d95163bbf821ed664b0ec15cd076cb1a2 [file] [log] [blame]
/*
* 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.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.hardware.CarSensorEvent;
import android.car.hardware.CarSensorManager;
import android.car.hardware.ICarSensorEventListener;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.content.res.Resources;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import com.android.car.CarLog;
import com.android.car.CarSensorService;
import com.android.car.CarServiceBase;
import com.android.car.CarServiceUtils;
import com.android.car.R;
import com.android.car.SystemActivityMonitoringService;
import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer;
import com.android.car.pm.CarAppMetadataReader.CarAppMetadataInfo;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
//TODO monitor app installing and refresh policy, bug: 31970400
public class CarPackageManagerService extends ICarPackageManager.Stub implements CarServiceBase {
static final boolean DBG_POLICY_SET = false;
static final boolean DBG_POLICY_CHECK = false;
static final boolean DBG_POLICY_ENFORCEMENT = false;
private final Context mContext;
private final SystemActivityMonitoringService mSystemActivityMonitoringService;
private final CarSensorService mSensorService;
private final PackageManager mPackageManager;
private final HandlerThread mHandlerThread;
private final PackageHandler mHandler;
private String mDefauiltActivityWhitelist;
/**
* 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> mSystemWhitelists = new HashMap<>();
@GuardedBy("this")
private LinkedList<AppBlockingPolicyProxy> mProxies;
@GuardedBy("this")
private final LinkedList<CarAppBlockingPolicy> mWaitingPolicies = new LinkedList<>();
private final boolean mEnableActivityBlocking;
private final ComponentName mActivityBlockingActivity;
private final ActivityLaunchListener mActivityLaunchListener = new ActivityLaunchListener();
private final SensorListener mDrivingStateListener = new SensorListener();
public CarPackageManagerService(Context context, CarSensorService sensorService,
SystemActivityMonitoringService systemActivityMonitoringService) {
mContext = context;
mSensorService = sensorService;
mSystemActivityMonitoringService = systemActivityMonitoringService;
mPackageManager = mContext.getPackageManager();
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);
}
@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*/);
}
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.assertPakcageName(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 isActivityAllowedWhileDriving(String packageName, String className) {
assertPackageAndClassName(packageName, className);
synchronized (this) {
if (DBG_POLICY_CHECK) {
Log.i(CarLog.TAG_PACKAGE, "isActivityAllowedWhileDriving" +
dumpPoliciesLocked(false));
}
AppBlockingPackageInfo info = searchFromBlacklistsLocked(packageName);
if (info != null) {
return false;
}
return isActivityInWhitelistsLocked(packageName, className);
}
}
@Override
public boolean isServiceAllowedWhileDriving(String packageName, String className) {
if (packageName == null) {
throw new IllegalArgumentException("Package name null");
}
synchronized (this) {
if (DBG_POLICY_CHECK) {
Log.i(CarLog.TAG_PACKAGE, "isServiceAllowedWhileDriving" +
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 (!mEnableActivityBlocking || !mDrivingStateListener.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 isActivityAllowedWhileDriving(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");
}
}
private AppBlockingPackageInfo searchFromBlacklistsLocked(String packageName) {
for (ClientPolicy policy : mClientPolicies.values()) {
AppBlockingPackageInfoWrapper wrapper = policy.blacklistsMap.get(packageName);
if (wrapper != null && wrapper.isMatching) {
return wrapper.info;
}
}
return null;
}
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 = mSystemWhitelists.get(packageName);
return (wrapper != null) ? wrapper.info : null;
}
private boolean isActivityInWhitelistsLocked(String packageName, String className) {
for (ClientPolicy policy : mClientPolicies.values()) {
if (isActivityInMapAndMatching(policy.whitelistsMap, packageName, className)) {
return true;
}
}
return isActivityInMapAndMatching(mSystemWhitelists, packageName, className);
}
private boolean isActivityInMapAndMatching(HashMap<String, AppBlockingPackageInfoWrapper> map,
String packageName, String className) {
AppBlockingPackageInfoWrapper wrapper = map.get(packageName);
if (wrapper == null || !wrapper.isMatching) {
return false;
}
return wrapper.info.isActivityCovered(className);
}
@Override
public void init() {
if (!mEnableActivityBlocking) {
return;
}
synchronized (this) {
mHandler.requestInit();
}
}
@Override
public void release() {
if (!mEnableActivityBlocking) {
return;
}
synchronized (this) {
mHandler.requestRelease();
// wait for release do be done. This guarantees that init is done.
try {
wait();
} catch (InterruptedException e) {
}
mSystemWhitelists.clear();
mClientPolicies.clear();
if (mProxies != null) {
for (AppBlockingPolicyProxy proxy : mProxies) {
proxy.disconnect();
}
mProxies.clear();
}
wakeupClientsWaitingForPolicySetitngLocked();
}
mSensorService.unregisterSensorListener(CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
mDrivingStateListener);
mSystemActivityMonitoringService.registerActivityLaunchListener(null);
}
// run from HandlerThread
private void doHandleInit() {
startAppBlockingPolicies();
generateSystemWhitelists();
try {
mSensorService.registerOrUpdateSensorListener(
CarSensorManager.SENSOR_TYPE_DRIVING_STATUS, 0, mDrivingStateListener);
} 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;
}
mDrivingStateListener.resetState();
mSystemActivityMonitoringService.registerActivityLaunchListener(
mActivityLaunchListener);
blockTopActivitiesIfNecessary();
}
private synchronized void doHandleRelease() {
notifyAll();
}
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;
}
/**
* Return list of whitelist including default activity. Key is package name while
* value is list of activities. If list is empty, whole activities in the package
* are whitelisted.
* @return
*/
private HashMap<String, Set<String>> parseConfigWhitelist() {
HashMap<String, Set<String>> packageToActivityMap = new HashMap<>();
Set<String> defaultActivity = new ArraySet<>();
defaultActivity.add(mActivityBlockingActivity.getClassName());
packageToActivityMap.put(mActivityBlockingActivity.getPackageName(), defaultActivity);
Resources res = mContext.getResources();
String whitelist = res.getString(R.string.defauiltActivityWhitelist);
mDefauiltActivityWhitelist = whitelist;
String[] entries = whitelist.split(",");
for (String entry : entries) {
String[] packageActivityPair = entry.split("/");
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;
}
private void generateSystemWhitelists() {
HashMap<String, AppBlockingPackageInfoWrapper> systemWhitelists = new HashMap<>();
HashMap<String, Set<String>> configWhitelist = parseConfigWhitelist();
// trust all system apps for services and trust all activities with car app meta-data.
List<PackageInfo> packages = mPackageManager.getInstalledPackages(0);
for (PackageInfo info : packages) {
if (info.applicationInfo != null && (info.applicationInfo.isSystemApp() ||
info.applicationInfo.isUpdatedSystemApp())) {
int flags = AppBlockingPackageInfo.FLAG_SYSTEM_APP;
Set<String> configActivitiesForPackage =
configWhitelist.get(info.packageName);
if (configActivitiesForPackage != null) {
if(configActivitiesForPackage.size() == 0) {
flags |= AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY;
}
} else {
configActivitiesForPackage = new ArraySet<>();
}
String[] activities = null;
// Go through meta data if whole activities are not allowed already
if ((flags & AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY) == 0) {
CarAppMetadataInfo metadataInfo = CarAppMetadataReader.parseMetadata(mContext,
info.packageName);
if (metadataInfo != null) {
if (metadataInfo.useAllActivities) {
flags |= AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY;
} else if(metadataInfo.activities != null) {
for (String activity : metadataInfo.activities) {
configActivitiesForPackage.add(activity);
}
}
}
if (configActivitiesForPackage.size() > 0) {
activities = configActivitiesForPackage.toArray(
new String[configActivitiesForPackage.size()]);
}
}
AppBlockingPackageInfo appBlockingInfo = new AppBlockingPackageInfo(
info.packageName, 0, 0, flags, null, activities);
AppBlockingPackageInfoWrapper wrapper = new AppBlockingPackageInfoWrapper(
appBlockingInfo, true);
systemWhitelists.put(info.packageName, wrapper);
}
}
synchronized (this) {
mSystemWhitelists.putAll(systemWhitelists);
}
}
private 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("ActivityRestricted:" + mDrivingStateListener.isRestricted());
writer.print(dumpPoliciesLocked(true));
}
}
private String dumpPoliciesLocked(boolean dumpAll) {
StringBuilder sb = new StringBuilder();
if (dumpAll) {
sb.append("**System white list**\n");
for (AppBlockingPackageInfoWrapper wrapper : mSystemWhitelists.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("**Default Whitelist string**\n");
sb.append(mDefauiltActivityWhitelist + "\n");
return sb.toString();
}
private void blockTopActivityIfNecessary(TopTaskInfoContainer topTask) {
boolean restricted = mDrivingStateListener.isRestricted();
if (!restricted) {
return;
}
doBlockTopActivityIfNotAllowed(topTask);
}
private void doBlockTopActivityIfNotAllowed(TopTaskInfoContainer topTask) {
boolean allowed = isActivityAllowedWhileDriving(
topTask.topActivity.getPackageName(),
topTask.topActivity.getClassName());
if (DBG_POLICY_ENFORCEMENT) {
Log.i(CarLog.TAG_PACKAGE, "new activity:" + topTask.toString() + " allowed:" + allowed);
}
if (!allowed) {
Log.i(CarLog.TAG_PACKAGE, "Current activity " + topTask.topActivity +
" not allowed, will block, number of tasks in stack:" +
topTask.stackInfo.taskIds.length);
Intent newActivityIntent = new Intent();
newActivityIntent.setComponent(mActivityBlockingActivity);
newActivityIntent.putExtra(
ActivityBlockingActivity.INTENT_KEY_BLOCKED_ACTIVITY,
topTask.topActivity.flattenToString());
mSystemActivityMonitoringService.blockActivity(topTask, newActivityIntent);
}
}
private void blockTopActivitiesIfNecessary() {
boolean restricted = mDrivingStateListener.isRestricted();
if (!restricted) {
return;
}
List<TopTaskInfoContainer> topTasks = mSystemActivityMonitoringService.getTopTasks();
for (TopTaskInfoContainer topTask : topTasks) {
doBlockTopActivityIfNotAllowed(topTask);
}
}
/**
* 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_SET_POLICY = 1;
private final int MSG_UPDATE_POLICY = 2;
private final int MSG_RELEASE = 3;
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);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INIT:
doHandleInit();
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);
}
}
private class SensorListener extends ICarSensorEventListener.Stub {
private int mLatestDrivingState;
private void resetState() {
CarSensorEvent lastEvent = mSensorService.getLatestSensorEvent(
CarSensorManager.SENSOR_TYPE_DRIVING_STATUS);
boolean shouldBlock = false;
synchronized (this) {
if (lastEvent == null) {
// When driving status is not available yet, do not block.
// This happens during bootup.
mLatestDrivingState = CarSensorEvent.DRIVE_STATUS_UNRESTRICTED;
} else {
mLatestDrivingState = lastEvent.intValues[0];
}
if (mLatestDrivingState != CarSensorEvent.DRIVE_STATUS_UNRESTRICTED) {
shouldBlock = true;
}
}
if (shouldBlock) {
blockTopActivitiesIfNecessary();
}
}
private synchronized boolean isRestricted() {
return mLatestDrivingState != CarSensorEvent.DRIVE_STATUS_UNRESTRICTED;
}
@Override
public void onSensorChanged(List<CarSensorEvent> events) {
resetState();
}
}
}