1 /*
2  * Copyright (C) 2018 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.phone;
18 
19 import android.app.role.RoleManager;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.PackageInfo;
25 import android.content.pm.PackageManager;
26 import android.content.pm.ResolveInfo;
27 import android.os.AsyncTask;
28 import android.os.Binder;
29 import android.os.Process;
30 import android.telephony.TelephonyManager;
31 import android.text.TextUtils;
32 import android.util.Log;
33 
34 import java.util.List;
35 
36 /**
37  * A helper to query activities of emergency assistance.
38  */
39 public class EmergencyAssistanceHelper {
40     private static final String TAG = EmergencyAssistanceHelper.class.getSimpleName();
41     /**
42      * Get intent action of target emergency app.
43      *
44      * @return A string of intent action to launch target emergency app.
45      */
getIntentAction()46     public static String getIntentAction() {
47         return TelephonyManager.ACTION_EMERGENCY_ASSISTANCE;
48     }
49 
50     /**
51      * Query activities of emergency assistance.
52      *
53      * @param context The context of the application.
54      * @return A list of {@link ResolveInfo} which is queried from default assistance package,
55      * or null if there is no installed system application of emergency assistance.
56      */
resolveAssistPackageAndQueryActivities(Context context)57     public static List<ResolveInfo> resolveAssistPackageAndQueryActivities(Context context) {
58         final String assistPackage = getDefaultEmergencyPackage(context);
59         List<ResolveInfo> infos = queryAssistActivities(context, assistPackage);
60         if (infos == null || infos.isEmpty()) {
61             PackageManager packageManager = context.getPackageManager();
62             Intent queryIntent = new Intent(getIntentAction());
63             infos = packageManager.queryIntentActivities(queryIntent, 0);
64 
65             PackageInfo bestMatch = null;
66             for (int i = 0; i < infos.size(); i++) {
67                 if (infos.get(i).activityInfo == null) continue;
68                 String packageName = infos.get(i).activityInfo.packageName;
69                 PackageInfo packageInfo;
70                 try {
71                     packageInfo = packageManager.getPackageInfo(packageName, 0);
72                 } catch (PackageManager.NameNotFoundException e) {
73                     continue;
74                 }
75                 // Get earliest installed system app.
76                 if (isSystemApp(packageInfo) && (bestMatch == null
77                         || bestMatch.firstInstallTime > packageInfo.firstInstallTime)) {
78                     bestMatch = packageInfo;
79                 }
80             }
81 
82             if (bestMatch != null) {
83                 setDefaultEmergencyPackageAsync(context, bestMatch.packageName);
84                 return queryAssistActivities(context, bestMatch.packageName);
85             } else {
86                 return null;
87             }
88         } else {
89             return infos;
90         }
91     }
92 
93     /**
94      * Compose {@link ComponentName} from {@link ResolveInfo}.
95      */
getComponentName(ResolveInfo resolveInfo)96     public static ComponentName getComponentName(ResolveInfo resolveInfo) {
97         if (resolveInfo == null || resolveInfo.activityInfo == null) return null;
98         return new ComponentName(resolveInfo.activityInfo.packageName,
99                 resolveInfo.activityInfo.name);
100     }
101 
queryAssistActivities(Context context, String assistPackage)102     private static List<ResolveInfo> queryAssistActivities(Context context, String assistPackage) {
103         List<ResolveInfo> infos = null;
104 
105         if (!TextUtils.isEmpty(assistPackage)) {
106             Intent queryIntent = new Intent(getIntentAction())
107                     .setPackage(assistPackage);
108             infos = context.getPackageManager().queryIntentActivities(queryIntent, 0);
109         }
110         return infos;
111     }
112 
isSystemApp(PackageInfo info)113     private static boolean isSystemApp(PackageInfo info) {
114         return info.applicationInfo != null
115                 && (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
116     }
117 
getDefaultEmergencyPackage(Context context)118     private static String getDefaultEmergencyPackage(Context context) {
119         long identity = Binder.clearCallingIdentity();
120         try {
121             List<String> roleHolders = context.getSystemService(RoleManager.class)
122                     .getRoleHolders(RoleManager.ROLE_EMERGENCY);
123             return roleHolders.isEmpty() ? null : roleHolders.get(0);
124         } finally {
125             Binder.restoreCallingIdentity(identity);
126         }
127     }
128 
setDefaultEmergencyPackageAsync(Context context, String pkgName)129     private static boolean setDefaultEmergencyPackageAsync(Context context, String pkgName) {
130         long identity = Binder.clearCallingIdentity();
131         try {
132             context.getSystemService(RoleManager.class).addRoleHolderAsUser(
133                     RoleManager.ROLE_EMERGENCY, pkgName, 0, Process.myUserHandle(),
134                     AsyncTask.THREAD_POOL_EXECUTOR, successful -> {
135                         if (!successful) {
136                             Log.e(TAG, "Failed to set emergency default app.");
137                         }
138                     });
139         } finally {
140             Binder.restoreCallingIdentity(identity);
141         }
142         return true;
143     }
144 }
145