blob: ae5ad7ea1261b2ecb5b2e9fdf51abdfe36c42c82 [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
110 @Override
Andrei Oneaea997f22019-09-12 18:08:59 +0100111 public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
112 CompatConfig.get().addOverrides(overrides, packageName);
atrostf69bbe12019-11-06 16:00:38 +0000113 killPackage(packageName);
114 }
115
116 @Override
117 public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
118 CompatConfig.get().addOverrides(overrides, packageName);
Andrei Oneaea997f22019-09-12 18:08:59 +0100119 }
120
121 @Override
122 public void clearOverrides(String packageName) {
123 CompatConfig config = CompatConfig.get();
124 config.removePackageOverrides(packageName);
atrostf69bbe12019-11-06 16:00:38 +0000125 killPackage(packageName);
126 }
127
128 @Override
Andrei Onea02d81c02019-11-18 14:21:44 +0000129 public void clearOverridesForTest(String packageName) {
130 CompatConfig config = CompatConfig.get();
131 config.removePackageOverrides(packageName);
132 }
133
134 @Override
atrostf69bbe12019-11-06 16:00:38 +0000135 public boolean clearOverride(long changeId, String packageName) {
136 boolean existed = CompatConfig.get().removeOverride(changeId, packageName);
137 killPackage(packageName);
138 return existed;
Andrei Oneaea997f22019-09-12 18:08:59 +0100139 }
140
141 @Override
Andrei Onea18041f782019-10-01 14:34:56 +0100142 public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
143 return CompatConfig.get().getAppConfig(appInfo);
144 }
145
146 @Override
147 public CompatibilityChangeInfo[] listAllChanges() {
148 return CompatConfig.get().dumpChanges();
149 }
150
atrostf69bbe12019-11-06 16:00:38 +0000151 /**
152 * Check whether the change is known to the compat config.
153 * @param changeId
154 * @return {@code true} if the change is known.
155 */
156 public boolean isKnownChangeId(long changeId) {
157 return CompatConfig.get().isKnownChangeId(changeId);
158
159 }
160
161 /**
162 * Retrieves the set of disabled changes for a given app. Any change ID not in the returned
163 * array is by default enabled for the app.
164 *
165 * @param appInfo The app in question
166 * @return A sorted long array of change IDs. We use a primitive array to minimize memory
167 * footprint: Every app process will store this array statically so we aim to reduce
168 * overhead as much as possible.
169 */
170 public long[] getDisabledChanges(ApplicationInfo appInfo) {
171 return CompatConfig.get().getDisabledChanges(appInfo);
172 }
173
174 /**
175 * Look up a change ID by name.
176 *
177 * @param name Name of the change to look up
178 * @return The change ID, or {@code -1} if no change with that name exists.
179 */
180 public long lookupChangeId(String name) {
181 return CompatConfig.get().lookupChangeId(name);
182 }
183
Andrei Onea18041f782019-10-01 14:34:56 +0100184 @Override
Andrei Onea6cd9d4d2019-07-17 19:03:10 +0100185 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
186 if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
Andrei Oneac61bf0c2019-07-10 16:43:09 +0100187 CompatConfig.get().dumpConfig(pw);
Andrei Onea6cd9d4d2019-07-17 19:03:10 +0100188 }
atroste36b1a12019-08-28 15:40:37 +0100189
atrost893f84d2019-09-30 11:29:44 +0100190 /**
191 * Clears information stored about events reported on behalf of an app.
192 * To be called once upon app start or end. A second call would be a no-op.
193 * @param appInfo the app to reset
194 */
195 public void resetReporting(ApplicationInfo appInfo) {
196 mChangeReporter.resetReportedChanges(appInfo.uid);
197 }
198
atrost50768542019-10-22 19:44:09 +0100199 private ApplicationInfo getApplicationInfo(String packageName, int userId) {
atrost12d0da32019-09-18 12:48:44 +0100200 try {
atrost50768542019-10-22 19:44:09 +0100201 return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0, userId);
atrost12d0da32019-09-18 12:48:44 +0100202 } catch (PackageManager.NameNotFoundException e) {
203 Slog.e(TAG, "No installed package " + packageName);
204 }
205 return null;
206 }
207
atrost64cacec2019-10-03 15:21:24 +0100208 private void reportChange(long changeId, int uid, int state) {
atrost6624ffa2019-09-20 16:30:07 +0100209 mChangeReporter.reportChange(uid, changeId, state);
atroste36b1a12019-08-28 15:40:37 +0100210 }
atrostf69bbe12019-11-06 16:00:38 +0000211
212 private void killPackage(String packageName) {
213 int uid = -1;
214 try {
215 uid = mContext.getPackageManager().getPackageUid(packageName, 0);
216 } catch (PackageManager.NameNotFoundException e) {
217 Slog.w(TAG, "Didn't find package " + packageName + " on device.", e);
218 return;
219 }
220
221 Slog.d(TAG, "Killing package " + packageName + " (UID " + uid + ").");
222 killUid(UserHandle.getAppId(uid),
223 UserHandle.USER_ALL, "PlatformCompat overrides");
224 }
225
226 private void killUid(int appId, int userId, String reason) {
227 final long identity = Binder.clearCallingIdentity();
228 try {
229 IActivityManager am = ActivityManager.getService();
230 if (am != null) {
231 try {
232 am.killUid(appId, userId, reason);
233 } catch (RemoteException e) {
234 /* ignore - same process */
235 }
236 }
237 } finally {
238 Binder.restoreCallingIdentity(identity);
239 }
240 }
Mathew Inwoode188acc2019-06-20 15:13:33 +0100241}