blob: 4a3d7d69d874b4b66d484082fe645062eca5eb37 [file] [log] [blame]
Mathew Inwoode188acc2019-06-20 15:13:33 +01001/*
2 * Copyright (C) 2019 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
17package com.android.server.compat;
18
atrostf69bbe12019-11-06 16:00:38 +000019import android.app.ActivityManager;
20import android.app.IActivityManager;
Andrei Onea6cd9d4d2019-07-17 19:03:10 +010021import android.content.Context;
Mathew Inwoode188acc2019-06-20 15:13:33 +010022import android.content.pm.ApplicationInfo;
atrost12d0da32019-09-18 12:48:44 +010023import android.content.pm.PackageManager;
atrostf69bbe12019-11-06 16:00:38 +000024import android.os.Binder;
25import android.os.RemoteException;
atrost50768542019-10-22 19:44:09 +010026import android.os.UserHandle;
Mathew Inwoode188acc2019-06-20 15:13:33 +010027import android.util.Slog;
atroste36b1a12019-08-28 15:40:37 +010028import android.util.StatsLog;
Mathew Inwoode188acc2019-06-20 15:13:33 +010029
atroste36b1a12019-08-28 15:40:37 +010030import com.android.internal.compat.ChangeReporter;
Andrei Oneaea997f22019-09-12 18:08:59 +010031import com.android.internal.compat.CompatibilityChangeConfig;
Andrei Onea18041f782019-10-01 14:34:56 +010032import com.android.internal.compat.CompatibilityChangeInfo;
atrosta6a4d602019-08-21 16:48:56 +010033import com.android.internal.compat.IPlatformCompat;
Andrei Onea6cd9d4d2019-07-17 19:03:10 +010034import com.android.internal.util.DumpUtils;
35
36import java.io.FileDescriptor;
37import java.io.PrintWriter;
38
Mathew Inwoode188acc2019-06-20 15:13:33 +010039/**
40 * System server internal API for gating and reporting compatibility changes.
41 */
Andrei Onea6cd9d4d2019-07-17 19:03:10 +010042public class PlatformCompat extends IPlatformCompat.Stub {
Mathew Inwoode188acc2019-06-20 15:13:33 +010043
44 private static final String TAG = "Compatibility";
45
Andrei Onea6cd9d4d2019-07-17 19:03:10 +010046 private final Context mContext;
atroste36b1a12019-08-28 15:40:37 +010047 private final ChangeReporter mChangeReporter;
Andrei Onea6cd9d4d2019-07-17 19:03:10 +010048
49 public PlatformCompat(Context context) {
50 mContext = context;
atrost6624ffa2019-09-20 16:30:07 +010051 mChangeReporter = new ChangeReporter(
52 StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
Andrei Onea6cd9d4d2019-07-17 19:03:10 +010053 }
54
55 @Override
56 public void reportChange(long changeId, ApplicationInfo appInfo) {
atrost64cacec2019-10-03 15:21:24 +010057 reportChange(changeId, appInfo.uid,
58 StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
Mathew Inwoode188acc2019-06-20 15:13:33 +010059 }
60
Andrei Onea6cd9d4d2019-07-17 19:03:10 +010061 @Override
atrost50768542019-10-22 19:44:09 +010062 public void reportChangeByPackageName(long changeId, String packageName, int userId) {
63 ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
atrost12d0da32019-09-18 12:48:44 +010064 if (appInfo == null) {
65 return;
66 }
67 reportChange(changeId, appInfo);
68 }
69
70 @Override
atrost64cacec2019-10-03 15:21:24 +010071 public void reportChangeByUid(long changeId, int uid) {
72 reportChange(changeId, uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
73 }
74
75 @Override
Andrei Onea6cd9d4d2019-07-17 19:03:10 +010076 public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
Mathew Inwoode188acc2019-06-20 15:13:33 +010077 if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) {
atrost64cacec2019-10-03 15:21:24 +010078 reportChange(changeId, appInfo.uid,
atroste36b1a12019-08-28 15:40:37 +010079 StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
Mathew Inwoode188acc2019-06-20 15:13:33 +010080 return true;
81 }
atrost64cacec2019-10-03 15:21:24 +010082 reportChange(changeId, appInfo.uid,
atroste36b1a12019-08-28 15:40:37 +010083 StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
Mathew Inwoode188acc2019-06-20 15:13:33 +010084 return false;
85 }
Andrei Onea6cd9d4d2019-07-17 19:03:10 +010086
87 @Override
atrost50768542019-10-22 19:44:09 +010088 public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
89 ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
atrost12d0da32019-09-18 12:48:44 +010090 if (appInfo == null) {
91 return true;
92 }
93 return isChangeEnabled(changeId, appInfo);
94 }
95
96 @Override
atrost64cacec2019-10-03 15:21:24 +010097 public boolean isChangeEnabledByUid(long changeId, int uid) {
98 String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
99 if (packages == null || packages.length == 0) {
100 return true;
101 }
102 boolean enabled = true;
103 for (String packageName : packages) {
atrost50768542019-10-22 19:44:09 +0100104 enabled = enabled && isChangeEnabledByPackageName(changeId, packageName,
105 UserHandle.getUserId(uid));
atrost64cacec2019-10-03 15:21:24 +0100106 }
107 return enabled;
108 }
109
atrost0be238b2019-11-11 16:54:04 +0000110 /**
111 * Register a listener for change state overrides. Only one listener per change is allowed.
112 *
113 * <p>{@code listener.onCompatChange(String)} method is guaranteed to be called with
114 * packageName before the app is killed upon an override change. The state of a change is not
115 * guaranteed to change when {@code listener.onCompatChange(String)} is called.
116 *
117 * @param changeId to get updates for
118 * @param listener the listener that will be called upon a potential change for package.
119 * @throws IllegalStateException if a listener was already registered for changeId
120 * @returns {@code true} if a change with changeId was already known, or (@code false}
121 * otherwise.
122 */
123 public boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
124 return CompatConfig.get().registerListener(changeId, listener);
125 }
126
atrost64cacec2019-10-03 15:21:24 +0100127 @Override
Andrei Oneaea997f22019-09-12 18:08:59 +0100128 public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
129 CompatConfig.get().addOverrides(overrides, packageName);
atrostf69bbe12019-11-06 16:00:38 +0000130 killPackage(packageName);
131 }
132
133 @Override
134 public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
135 CompatConfig.get().addOverrides(overrides, packageName);
Andrei Oneaea997f22019-09-12 18:08:59 +0100136 }
137
138 @Override
139 public void clearOverrides(String packageName) {
140 CompatConfig config = CompatConfig.get();
141 config.removePackageOverrides(packageName);
atrostf69bbe12019-11-06 16:00:38 +0000142 killPackage(packageName);
143 }
144
145 @Override
Andrei Onea02d81c02019-11-18 14:21:44 +0000146 public void clearOverridesForTest(String packageName) {
147 CompatConfig config = CompatConfig.get();
148 config.removePackageOverrides(packageName);
149 }
150
151 @Override
atrostf69bbe12019-11-06 16:00:38 +0000152 public boolean clearOverride(long changeId, String packageName) {
153 boolean existed = CompatConfig.get().removeOverride(changeId, packageName);
154 killPackage(packageName);
155 return existed;
Andrei Oneaea997f22019-09-12 18:08:59 +0100156 }
157
158 @Override
Andrei Onea18041f782019-10-01 14:34:56 +0100159 public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
160 return CompatConfig.get().getAppConfig(appInfo);
161 }
162
163 @Override
164 public CompatibilityChangeInfo[] listAllChanges() {
165 return CompatConfig.get().dumpChanges();
166 }
167
atrostf69bbe12019-11-06 16:00:38 +0000168 /**
169 * Check whether the change is known to the compat config.
170 * @param changeId
171 * @return {@code true} if the change is known.
172 */
173 public boolean isKnownChangeId(long changeId) {
174 return CompatConfig.get().isKnownChangeId(changeId);
175
176 }
177
178 /**
179 * Retrieves the set of disabled changes for a given app. Any change ID not in the returned
180 * array is by default enabled for the app.
181 *
182 * @param appInfo The app in question
183 * @return A sorted long array of change IDs. We use a primitive array to minimize memory
184 * footprint: Every app process will store this array statically so we aim to reduce
185 * overhead as much as possible.
186 */
187 public long[] getDisabledChanges(ApplicationInfo appInfo) {
188 return CompatConfig.get().getDisabledChanges(appInfo);
189 }
190
191 /**
192 * Look up a change ID by name.
193 *
194 * @param name Name of the change to look up
195 * @return The change ID, or {@code -1} if no change with that name exists.
196 */
197 public long lookupChangeId(String name) {
198 return CompatConfig.get().lookupChangeId(name);
199 }
200
Andrei Onea18041f782019-10-01 14:34:56 +0100201 @Override
Andrei Onea6cd9d4d2019-07-17 19:03:10 +0100202 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
203 if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
Andrei Oneac61bf0c2019-07-10 16:43:09 +0100204 CompatConfig.get().dumpConfig(pw);
Andrei Onea6cd9d4d2019-07-17 19:03:10 +0100205 }
atroste36b1a12019-08-28 15:40:37 +0100206
atrost893f84d2019-09-30 11:29:44 +0100207 /**
208 * Clears information stored about events reported on behalf of an app.
209 * To be called once upon app start or end. A second call would be a no-op.
210 * @param appInfo the app to reset
211 */
212 public void resetReporting(ApplicationInfo appInfo) {
213 mChangeReporter.resetReportedChanges(appInfo.uid);
214 }
215
atrost50768542019-10-22 19:44:09 +0100216 private ApplicationInfo getApplicationInfo(String packageName, int userId) {
atrost12d0da32019-09-18 12:48:44 +0100217 try {
atrost50768542019-10-22 19:44:09 +0100218 return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0, userId);
atrost12d0da32019-09-18 12:48:44 +0100219 } catch (PackageManager.NameNotFoundException e) {
220 Slog.e(TAG, "No installed package " + packageName);
221 }
222 return null;
223 }
224
atrost64cacec2019-10-03 15:21:24 +0100225 private void reportChange(long changeId, int uid, int state) {
atrost6624ffa2019-09-20 16:30:07 +0100226 mChangeReporter.reportChange(uid, changeId, state);
atroste36b1a12019-08-28 15:40:37 +0100227 }
atrostf69bbe12019-11-06 16:00:38 +0000228
229 private void killPackage(String packageName) {
230 int uid = -1;
231 try {
232 uid = mContext.getPackageManager().getPackageUid(packageName, 0);
233 } catch (PackageManager.NameNotFoundException e) {
234 Slog.w(TAG, "Didn't find package " + packageName + " on device.", e);
235 return;
236 }
237
238 Slog.d(TAG, "Killing package " + packageName + " (UID " + uid + ").");
239 killUid(UserHandle.getAppId(uid),
240 UserHandle.USER_ALL, "PlatformCompat overrides");
241 }
242
243 private void killUid(int appId, int userId, String reason) {
244 final long identity = Binder.clearCallingIdentity();
245 try {
246 IActivityManager am = ActivityManager.getService();
247 if (am != null) {
248 try {
249 am.killUid(appId, userId, reason);
250 } catch (RemoteException e) {
251 /* ignore - same process */
252 }
253 }
254 } finally {
255 Binder.restoreCallingIdentity(identity);
256 }
257 }
Mathew Inwoode188acc2019-06-20 15:13:33 +0100258}