blob: 3d247f7f7f6642796899bb41fd969e32bac4631c [file] [log] [blame]
/*
* Copyright (C) 2021 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;
import android.annotation.Nullable;
import android.car.builtin.content.pm.PackageManagerHelper;
import android.car.builtin.util.Slogf;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.om.OverlayManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
/** Context for updatable package */
public class UpdatablePackageContext extends ContextWrapper {
// TODO(b/216134347): Find a better way to find the package.
private static final String[] UPDATABLE_CAR_SERVICE_PACKAGE_CANDIDATES =
{ "com.android.car.updatable", "com.google.android.car.updatable" };
private static final String TAG = UpdatablePackageContext.class.getSimpleName();
// This is the package context of the com.android.car.updatable
private final Context mPackageContext;
/** Create context for updatable package */
public static UpdatablePackageContext create(Context baseContext) {
Context packageContext;
try {
PackageInfo info = findUpdatableServicePackage(baseContext);
if (info == null || info.applicationInfo == null || !(info.applicationInfo.isSystemApp()
|| info.applicationInfo.isUpdatedSystemApp())) {
throw new IllegalStateException(
"Updated car service package is not usable:" + ((info == null)
? "do not exist" : info.applicationInfo));
}
// Enable correct RRO package
enableRROForCarServiceUpdatable(baseContext);
// CONTEXT_IGNORE_SECURITY: UID is different but ok as the package is trustable system
// app
packageContext = baseContext.createPackageContext(info.packageName,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
} catch (Exception e) {
throw new RuntimeException("Cannot load updatable package code", e);
}
return new UpdatablePackageContext(baseContext, packageContext);
}
@Nullable
private static PackageInfo findUpdatableServicePackage(Context baseContext) {
PackageInfo info = null;
for (int i = 0; i < UPDATABLE_CAR_SERVICE_PACKAGE_CANDIDATES.length; i++) {
try {
info = baseContext.getPackageManager().getPackageInfo(
UPDATABLE_CAR_SERVICE_PACKAGE_CANDIDATES[i], /* flags= */ 0);
if (info != null) {
break;
}
} catch (PackageManager.NameNotFoundException e) {
// ignore
}
}
return info;
}
private static void enableRROForCarServiceUpdatable(Context baseContext) {
String packageName = SystemProperties.get(
PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGE_NAME,
/* default= */ null);
if (TextUtils.isEmpty(packageName)) {
// read only property not defined. No need to dynamically overlay resources.
Slogf.i(TAG, " %s is not set. No need to dynamically overlay resources.",
PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGE_NAME);
return;
}
// check package
try {
PackageInfo info = baseContext.getPackageManager().getPackageInfo(packageName, 0);
if (info == null || info.applicationInfo == null
|| !(PackageManagerHelper.isSystemApp(info.applicationInfo)
|| PackageManagerHelper.isUpdatedSystemApp(info.applicationInfo)
|| PackageManagerHelper.isOemApp(info.applicationInfo)
|| PackageManagerHelper.isOdmApp(info.applicationInfo)
|| PackageManagerHelper.isVendorApp(info.applicationInfo)
|| PackageManagerHelper.isProductApp(info.applicationInfo)
|| PackageManagerHelper.isSystemExtApp(info.applicationInfo))) {
Slogf.i(TAG, "%s is not usable: %s", packageName, ((info == null)
? "package do not exist" : info.applicationInfo));
return;
}
} catch (Exception e) {
Slogf.w(TAG, e, "couldn't find package: %s", packageName);
return;
}
// package is valid. Enable RRO. This class is called for each user, so need to enable RRO
// for system and current user separately.
UserHandle user = baseContext.getUser();
try {
OverlayManager manager = baseContext.getSystemService(OverlayManager.class);
manager.setEnabled(packageName, true, user);
Slogf.i(TAG, "RRO package %s is enabled for User %s", packageName, user);
} catch (Exception e) {
Slogf.w(TAG, e, "RRO package %s is NOT enabled for User %s", packageName, user);
}
}
private UpdatablePackageContext(Context baseContext, Context packageContext) {
super(baseContext);
mPackageContext = packageContext;
}
@Override
public AssetManager getAssets() {
return mPackageContext.getAssets();
}
@Override
public Resources getResources() {
return mPackageContext.getResources();
}
@Override
public ClassLoader getClassLoader() {
// This context cannot load code from builtin any more.
return mPackageContext.getClassLoader();
}
}