• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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