1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.crashrecovery; 18 19 import android.annotation.AnyThread; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.content.pm.PackageInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.VersionedPackage; 26 import android.net.ConnectivityModuleConnector; 27 import android.text.TextUtils; 28 import android.util.Slog; 29 30 import com.android.server.PackageWatchdog; 31 import com.android.server.pm.ApexManager; 32 33 import java.util.Collections; 34 import java.util.List; 35 36 /** 37 * Provides helper methods for the CrashRecovery APEX 38 * 39 * @hide 40 */ 41 public final class CrashRecoveryHelper { 42 private static final String TAG = "CrashRecoveryHelper"; 43 44 private final ApexManager mApexManager; 45 private final Context mContext; 46 private final ConnectivityModuleConnector mConnectivityModuleConnector; 47 48 49 /** @hide */ CrashRecoveryHelper(@onNull Context context)50 public CrashRecoveryHelper(@NonNull Context context) { 51 mContext = context; 52 mApexManager = ApexManager.getInstance(); 53 mConnectivityModuleConnector = ConnectivityModuleConnector.getInstance(); 54 } 55 56 /** 57 * Returns true if the package name is the name of a module. 58 * If the package is an APK inside an APEX then it will use the parent's APEX package name 59 * do determine if it is a module or not. 60 * @hide 61 */ 62 @AnyThread isModule(@onNull String packageName)63 public boolean isModule(@NonNull String packageName) { 64 String apexPackageName = 65 mApexManager.getActiveApexPackageNameContainingPackage(packageName); 66 if (apexPackageName != null) { 67 packageName = apexPackageName; 68 } 69 70 PackageManager pm = mContext.getPackageManager(); 71 try { 72 return pm.getModuleInfo(packageName, 0) != null; 73 } catch (PackageManager.NameNotFoundException ignore) { 74 return false; 75 } 76 } 77 78 /** 79 * Register health listeners for explicit package failures. 80 * Currently only registering for Connectivity Module health. 81 * @hide 82 */ registerConnectivityModuleHealthListener(@onNull int failureReason)83 public void registerConnectivityModuleHealthListener(@NonNull int failureReason) { 84 // register listener for ConnectivityModule 85 mConnectivityModuleConnector.registerHealthListener( 86 packageName -> { 87 final VersionedPackage pkg = getVersionedPackage(packageName); 88 if (pkg == null) { 89 Slog.wtf(TAG, "NetworkStack failed but could not find its package"); 90 return; 91 } 92 final List<VersionedPackage> pkgList = Collections.singletonList(pkg); 93 PackageWatchdog.getInstance(mContext).onPackageFailure(pkgList, failureReason); 94 }); 95 } 96 97 @Nullable getVersionedPackage(String packageName)98 private VersionedPackage getVersionedPackage(String packageName) { 99 final PackageManager pm = mContext.getPackageManager(); 100 if (pm == null || TextUtils.isEmpty(packageName)) { 101 return null; 102 } 103 try { 104 final long versionCode = getPackageInfo(packageName).getLongVersionCode(); 105 return new VersionedPackage(packageName, versionCode); 106 } catch (PackageManager.NameNotFoundException e) { 107 return null; 108 } 109 } 110 111 /** 112 * Gets PackageInfo for the given package. Matches any user and apex. 113 * 114 * @throws PackageManager.NameNotFoundException if no such package is installed. 115 */ getPackageInfo(String packageName)116 private PackageInfo getPackageInfo(String packageName) 117 throws PackageManager.NameNotFoundException { 118 PackageManager pm = mContext.getPackageManager(); 119 try { 120 // The MATCH_ANY_USER flag doesn't mix well with the MATCH_APEX 121 // flag, so make two separate attempts to get the package info. 122 // We don't need both flags at the same time because we assume 123 // apex files are always installed for all users. 124 return pm.getPackageInfo(packageName, PackageManager.MATCH_ANY_USER); 125 } catch (PackageManager.NameNotFoundException e) { 126 return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX); 127 } 128 } 129 } 130