blob: 01e5dcaea11496b21279b50fab8259513a39f067 [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 android.car.content.pm;
import static android.car.Car.PERMISSION_CONTROL_APP_BLOCKING;
import static android.car.CarLibLog.TAG_CAR;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.car.Car;
import android.car.CarManagerBase;
import android.car.CarVersion;
import android.car.annotation.AddedInOrBefore;
import android.car.annotation.ApiRequirements;
import android.content.ComponentName;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
/**
* Provides car specific API related with package management.
*/
public final class CarPackageManager extends CarManagerBase {
private static final String TAG = CarPackageManager.class.getSimpleName();
/**
* Flag for {@link #setAppBlockingPolicy(String, CarAppBlockingPolicy, int)}. When this
* flag is set, the call will be blocked until policy is set to system. This can take time
* and the flag cannot be used in main thread.
*
* @hide
* @deprecated see the {@link #setAppBlockingPolicy(String, CarAppBlockingPolicy, int)}
* documentation for alternative mechanism.
*/
@SystemApi
@Deprecated
@AddedInOrBefore(majorVersion = 33)
public static final int FLAG_SET_POLICY_WAIT_FOR_CHANGE = 0x1;
/**
* Flag for {@link #setAppBlockingPolicy(String, CarAppBlockingPolicy, int)}. When this
* flag is set, passed policy is added to existing policy set from the current package.
* If none of {@link #FLAG_SET_POLICY_ADD} or {@link #FLAG_SET_POLICY_REMOVE} is set, existing
* policy is replaced. Note that policy per each package is always replaced and will not be
* added.
*
* @hide
* @deprecated see the {@link #setAppBlockingPolicy(String, CarAppBlockingPolicy, int)}
* documentation for alternative mechanism.
*/
@SystemApi
@Deprecated
@AddedInOrBefore(majorVersion = 33)
public static final int FLAG_SET_POLICY_ADD = 0x2;
/**
* Flag for {@link #setAppBlockingPolicy(String, CarAppBlockingPolicy, int)}. When this
* flag is set, passed policy is removed from existing policy set from the current package.
* If none of {@link #FLAG_SET_POLICY_ADD} or {@link #FLAG_SET_POLICY_REMOVE} is set, existing
* policy is replaced.
*
* @hide
* @deprecated see the {@link #setAppBlockingPolicy(String, CarAppBlockingPolicy, int)}
* documentation for alternative mechanism.
*/
@SystemApi
@Deprecated
@AddedInOrBefore(majorVersion = 33)
public static final int FLAG_SET_POLICY_REMOVE = 0x4;
/**
* Name of blocked activity.
*
* @hide
*/
@AddedInOrBefore(majorVersion = 33)
public static final String BLOCKING_INTENT_EXTRA_BLOCKED_ACTIVITY_NAME = "blocked_activity";
/**
* int task id of the blocked task.
*
* @hide
*/
@AddedInOrBefore(majorVersion = 33)
public static final String BLOCKING_INTENT_EXTRA_BLOCKED_TASK_ID = "blocked_task_id";
/**
* Name of root activity of blocked task.
*
* @hide
*/
@AddedInOrBefore(majorVersion = 33)
public static final String BLOCKING_INTENT_EXTRA_ROOT_ACTIVITY_NAME = "root_activity_name";
/**
* Boolean indicating whether the root activity is distraction-optimized (DO).
* Blocking screen should show a button to restart the task if {@code true}.
*
* @hide
*/
@AddedInOrBefore(majorVersion = 33)
public static final String BLOCKING_INTENT_EXTRA_IS_ROOT_ACTIVITY_DO = "is_root_activity_do";
/**
* int display id of the blocked task.
*
* @hide
*/
@AddedInOrBefore(majorVersion = 33)
public static final String BLOCKING_INTENT_EXTRA_DISPLAY_ID = "display_id";
/**
* Represents support of all regions for driving safety.
*
* @hide
*/
@AddedInOrBefore(majorVersion = 33)
public static final String DRIVING_SAFETY_REGION_ALL = "android.car.drivingsafetyregion.all";
/**
* Metadata which Activity can use to specify the driving safety regions it is supporting.
*
* <p>Definition of driving safety region is car OEM specific for now and only OEM apps
* should use this. If there are multiple regions, it should be comma separated. Not specifying
* this means supporting all regions.
*
* <p>Some examples are:
* <meta-data android:name="android.car.drivingsafetyregions"
* android:value="com.android.drivingsafetyregion.1,com.android.drivingsafetyregion.2"/>
*
* @hide
*/
@AddedInOrBefore(majorVersion = 33)
public static final String DRIVING_SAFETY_ACTIVITY_METADATA_REGIONS =
"android.car.drivingsafetyregions";
/**
* Internal error code for throwing {@code NameNotFoundException} from service.
*
* @hide
*/
@AddedInOrBefore(majorVersion = 33)
public static final int ERROR_CODE_NO_PACKAGE = -100;
/**
* Manifest metadata used to specify the minimum major and minor Car API version an app is
* targeting.
*
* <p>Format is in the form {@code major:minor} or {@code major}.
*
* <p>For example, for {@link Build.VERSION_CODES#TIRAMISU Android 13}, it would be:
* <code><meta-data android:name="android.car.targetCarVersion" android:value="33"/></code>
*
* <p>Or:
*
* <code><meta-data android:name="android.car.targetCarVersion" android:value="33:0"/></code>
*
* <p>And for {@link Build.VERSION_CODES#TIRAMISU Android 13} first update:
*
* <code><meta-data android:name="android.car.targetCarVersion" android:value="33:1"/></code>
*
* @hide
*/
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_1,
minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
@SystemApi
public static final String MANIFEST_METADATA_TARGET_CAR_VERSION =
"android.car.targetCarVersion";
/** @hide */
@IntDef(flag = true,
value = {FLAG_SET_POLICY_WAIT_FOR_CHANGE, FLAG_SET_POLICY_ADD, FLAG_SET_POLICY_REMOVE})
@Retention(RetentionPolicy.SOURCE)
public @interface SetPolicyFlags {}
private final ICarPackageManager mService;
/** @hide */
public CarPackageManager(Car car, IBinder service) {
this(car, ICarPackageManager.Stub.asInterface(service));
}
/** @hide */
@VisibleForTesting
public CarPackageManager(Car car, ICarPackageManager service) {
super(car);
mService = service;
}
/** @hide */
@Override
@AddedInOrBefore(majorVersion = 33)
public void onCarDisconnected() {
// nothing to do
}
/**
* Set Application blocking policy for system app. {@link #FLAG_SET_POLICY_ADD} or
* {@link #FLAG_SET_POLICY_REMOVE} flag allows adding or removing from already set policy. When
* none of these flags are set, it will completely replace existing policy for each package
* specified.
* When {@link #FLAG_SET_POLICY_WAIT_FOR_CHANGE} flag is set, this call will be blocked
* until the policy is set to system and become effective. Otherwise, the call will start
* changing the policy but it will be completed asynchronously and the call will return
* without waiting for system level policy change.
*
* @param packageName Package name of the client. If wrong package name is passed, exception
* will be thrown. This name is used to update the policy.
* @param policy
* @param flags
* @throws SecurityException if caller has no permission.
* @throws IllegalArgumentException For wrong or invalid arguments.
* @throws IllegalStateException If {@link #FLAG_SET_POLICY_WAIT_FOR_CHANGE} is set while
* called from main thread.
* @hide
* @deprecated It is no longer possible to change the app blocking policy at runtime. The first
* choice to mark an activity as safe for driving should always be to to include
* {@code <meta-data android:name="distractionOptimized" android:value="true"/>} in its
* manifest. All other activities will be blocked whenever driving restrictions are required. If
* an activity's manifest cannot be changed, then you can explicitly make an exception to its
* behavior using the build-time XML configuration. Allow or deny specific activities by
* changing the appropriate value ({@code R.string.activityAllowlist},
* {@code R.string.activityDenylist}) within the
* {@code packages/services/Car/service/res/values/config.xml} overlay.
*/
@SystemApi
@Deprecated
@AddedInOrBefore(majorVersion = 33)
public void setAppBlockingPolicy(
String packageName, CarAppBlockingPolicy policy, @SetPolicyFlags int flags) {
if ((flags & FLAG_SET_POLICY_WAIT_FOR_CHANGE) != 0
&& Looper.getMainLooper().isCurrentThread()) {
throw new IllegalStateException(
"FLAG_SET_POLICY_WAIT_FOR_CHANGE cannot be used in main thread");
}
try {
mService.setAppBlockingPolicy(packageName, policy, flags);
} catch (RemoteException e) {
handleRemoteExceptionFromCarService(e);
}
}
/**
* Restarts the requested task. If task with {@code taskId} does not exist, do nothing.
*
* <p>This requires {@code android.permission.REAL_GET_TASKS} permission.
*
* @hide
*/
@AddedInOrBefore(majorVersion = 33)
public void restartTask(int taskId) {
try {
mService.restartTask(taskId);
} catch (RemoteException e) {
handleRemoteExceptionFromCarService(e);
}
}
/**
* Check if finishing Activity will lead into safe Activity (=allowed Activity) to be shown.
* This can be used by unsafe activity blocking Activity to check if finishing itself can
* lead into being launched again due to unsafe activity shown. Note that checking this does not
* guarantee that blocking will not be done as driving state can change after this call is made.
*
* @param activityName
* @return true if there is a safe Activity (or car is stopped) in the back of task stack
* so that finishing the Activity will not trigger another Activity blocking. If
* the given Activity is not in foreground, then it will return true as well as
* finishing the Activity will not make any difference.
*
* @hide
*/
@SystemApi
@AddedInOrBefore(majorVersion = 33)
public boolean isActivityBackedBySafeActivity(ComponentName activityName) {
try {
return mService.isActivityBackedBySafeActivity(activityName);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, false);
}
}
/**
* Enable/Disable Activity Blocking. This is to provide an option for toggling app blocking
* behavior for development purposes.
* @hide
*/
@TestApi
@AddedInOrBefore(majorVersion = 33)
public void setEnableActivityBlocking(boolean enable) {
try {
mService.setEnableActivityBlocking(enable);
} catch (RemoteException e) {
handleRemoteExceptionFromCarService(e);
}
}
/**
* Returns whether an activity is distraction optimized, i.e, allowed in a restricted
* driving state.
*
* @param packageName the activity's {@link android.content.pm.ActivityInfo#packageName}.
* @param className the activity's {@link android.content.pm.ActivityInfo#name}.
* @return true if the activity is distraction optimized, false if it isn't or if the value
* could not be determined.
*/
@AddedInOrBefore(majorVersion = 33)
public boolean isActivityDistractionOptimized(String packageName, String className) {
try {
return mService.isActivityDistractionOptimized(packageName, className);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, false);
}
}
/**
* Returns whether the given {@link PendingIntent} represents an activity that is distraction
* optimized, i.e, allowed in a restricted driving state.
*
* @param pendingIntent the {@link PendingIntent} to check.
* @return true if the pending intent represents an activity that is distraction optimized,
* false if it isn't or if the value could not be determined.
*/
@AddedInOrBefore(majorVersion = 33)
public boolean isPendingIntentDistractionOptimized(@NonNull PendingIntent pendingIntent) {
try {
return mService.isPendingIntentDistractionOptimized(pendingIntent);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, false);
}
}
/**
* Check if given service is distraction optimized, i.e, allowed in a restricted
* driving state.
*
* @param packageName
* @param className
* @return
*/
@AddedInOrBefore(majorVersion = 33)
public boolean isServiceDistractionOptimized(String packageName, String className) {
try {
return mService.isServiceDistractionOptimized(packageName, className);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, false);
}
}
/**
* Returns the current driving safety region of the system. It will return OEM specific regions
* or {@link #DRIVING_SAFETY_REGION_ALL} when all regions are supported.
*
* <p> System's driving safety region is static and does not change until system restarts.
*
* @hide
*/
@RequiresPermission(anyOf = {PERMISSION_CONTROL_APP_BLOCKING,
Car.PERMISSION_CAR_DRIVING_STATE})
@NonNull
@AddedInOrBefore(majorVersion = 33)
public String getCurrentDrivingSafetyRegion() {
try {
return mService.getCurrentDrivingSafetyRegion();
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, DRIVING_SAFETY_REGION_ALL);
}
}
/**
* Enables or disables bypassing of unsafe {@code Activity} blocking for a specific
* {@code Activity} temporarily.
*
* <p> Enabling bypassing only lasts until the user stops using the car or until a user
* switching happens. Apps like launcher may ask user's consent to bypass. Note that bypassing
* is done for the package for all android users including the current user and user 0.
* <p> If bypassing is disabled and if the unsafe app is in foreground with driving state, the
* app will be immediately blocked.
*
* @param packageName Target package name.
* @param activityClassName Target Activity name (in full class name).
* @param bypass Bypass {@code Activity} blocking when true. Do not bypass anymore when false.
* @param userId User Id where the package is installed. Even if the bypassing is enabled for
* all android users, the package should be available for the specified user id.
*
* @throws NameNotFoundException If the given package / Activity class does not exist for the
* user.
*
* @hide
*/
@RequiresPermission(allOf = {PERMISSION_CONTROL_APP_BLOCKING,
android.Manifest.permission.QUERY_ALL_PACKAGES})
@AddedInOrBefore(majorVersion = 33)
public void controlTemporaryActivityBlockingBypassingAsUser(String packageName,
String activityClassName, boolean bypass, @UserIdInt int userId)
throws NameNotFoundException {
try {
mService.controlOneTimeActivityBlockingBypassingAsUser(packageName, activityClassName,
bypass, userId);
} catch (ServiceSpecificException e) {
handleServiceSpecificFromCarService(e, packageName, activityClassName, userId);
} catch (RemoteException e) {
handleRemoteExceptionFromCarService(e);
}
}
/**
* Returns all supported driving safety regions for the given Activity. If the Activity supports
* all regions, it will only include {@link #DRIVING_SAFETY_REGION_ALL}.
*
* <p> The permission specification requires {@code PERMISSION_CONTROL_APP_BLOCKING} and
* {@code QUERY_ALL_PACKAGES} but this API will also work if the client has
* {@link Car#PERMISSION_CAR_DRIVING_STATE} and {@code QUERY_ALL_PACKAGES} permissions.
*
* @param packageName Target package name.
* @param activityClassName Target Activity name (in full class name).
* @param userId Android user Id to check the package.
*
* @return Empty list if the Activity does not support driving safety (=no
* {@code distractionOptimized} metadata). Otherwise returns full list of all supported
* regions.
*
* @throws NameNotFoundException If the given package / Activity class does not exist for the
* user.
*
* @hide
*/
@RequiresPermission(allOf = {PERMISSION_CONTROL_APP_BLOCKING,
android.Manifest.permission.QUERY_ALL_PACKAGES})
@NonNull
@AddedInOrBefore(majorVersion = 33)
public List<String> getSupportedDrivingSafetyRegionsForActivityAsUser(String packageName,
String activityClassName, @UserIdInt int userId) throws NameNotFoundException {
try {
return mService.getSupportedDrivingSafetyRegionsForActivityAsUser(packageName,
activityClassName, userId);
} catch (ServiceSpecificException e) {
handleServiceSpecificFromCarService(e, packageName, activityClassName, userId);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST);
}
return Collections.EMPTY_LIST; // cannot reach here but the compiler complains.
}
/**
* Gets the Car API version targeted by the given package (as defined by
* {@link #MANIFEST_METADATA_TARGET_CAR_VERSION}.
*
* <p>If the app manifest doesn't contain the {@link #MANIFEST_METADATA_TARGET_CAR_VERSION}
* metadata attribute, the attribute format is invalid, the returned {@code CarVersion} will
* be using the
* {@link android.content.pm.ApplicationInfo#targetSdkVersion target platform version} as major
* and {@code 0} as minor instead.
*
* <p><b>Note: </b>to get the target {@link CarVersion} for your own app, use
* {@link #getTargetCarVersion()} instead.
* @return Car API version targeted by the given package (as described above).
*
* @throws NameNotFoundException If the given package does not exist for the user.
*
* @hide
*/
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_1,
minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
@SystemApi
@RequiresPermission(Manifest.permission.QUERY_ALL_PACKAGES)
@NonNull
public CarVersion getTargetCarVersion(@NonNull String packageName)
throws NameNotFoundException {
try {
return mService.getTargetCarVersion(packageName);
} catch (ServiceSpecificException e) {
Log.w(TAG, "Failed to get CarVersion for " + packageName, e);
handleServiceSpecificFromCarService(e, packageName);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
return null; // cannot reach here but the compiler complains.
}
/**
* Gets the Car API version targeted by app (as defined by
* {@link #MANIFEST_METADATA_TARGET_CAR_VERSION}.
*
* <p>If the app manifest doesn't contain the {@link #MANIFEST_METADATA_TARGET_CAR_VERSION}
* metadata attribute, the attribute format is invalid, the returned {@code CarVersion} will
* be using the {@link android.content.pm.ApplicationInfo#targetSdkVersion target platform
* version} as major and {@code 0} as minor instead.
*
* @return targeted Car API version (as defined above)
*/
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_1,
minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
@NonNull
public CarVersion getTargetCarVersion() {
String pkgName = mCar.getContext().getPackageName();
try {
return mService.getSelfTargetCarVersion(pkgName);
} catch (RemoteException e) {
Log.w(TAG_CAR, "Car service threw exception calling getTargetCarVersion(" + pkgName
+ ")", e);
e.rethrowFromSystemServer();
return null;
}
}
private void handleServiceSpecificFromCarService(ServiceSpecificException e,
String packageName) throws NameNotFoundException {
if (e.errorCode == ERROR_CODE_NO_PACKAGE) {
throw new NameNotFoundException(
"cannot find " + packageName + " for user " + Process.myUserHandle());
}
// don't know what this is
throw new IllegalStateException(e);
}
private static void handleServiceSpecificFromCarService(ServiceSpecificException e,
String packageName, String activityClassName, @UserIdInt int userId)
throws NameNotFoundException {
if (e.errorCode == ERROR_CODE_NO_PACKAGE) {
throw new NameNotFoundException(
"cannot find " + packageName + "/" + activityClassName + " for user id:"
+ userId);
}
// don't know what this is
throw new IllegalStateException(e);
}
}