1 package com.android.server.am; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.util.HashMap; 7 import java.util.HashSet; 8 import java.util.Iterator; 9 import java.util.Map; 10 11 import org.xmlpull.v1.XmlPullParser; 12 import org.xmlpull.v1.XmlPullParserException; 13 import org.xmlpull.v1.XmlSerializer; 14 15 import com.android.internal.os.AtomicFile; 16 import com.android.internal.util.FastXmlSerializer; 17 18 import android.app.ActivityManager; 19 import android.app.AppGlobals; 20 import android.content.pm.ActivityInfo; 21 import android.content.pm.ApplicationInfo; 22 import android.content.pm.IPackageManager; 23 import android.content.res.CompatibilityInfo; 24 import android.os.Handler; 25 import android.os.Message; 26 import android.os.RemoteException; 27 import android.util.Slog; 28 import android.util.Xml; 29 30 public class CompatModePackages { 31 private final String TAG = ActivityManagerService.TAG; 32 private final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION; 33 34 private final ActivityManagerService mService; 35 private final AtomicFile mFile; 36 37 // Compatibility state: no longer ask user to select the mode. 38 public static final int COMPAT_FLAG_DONT_ASK = 1<<0; 39 // Compatibility state: compatibility mode is enabled. 40 public static final int COMPAT_FLAG_ENABLED = 1<<1; 41 42 private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>(); 43 44 private static final int MSG_WRITE = 1; 45 46 private final Handler mHandler = new Handler() { 47 @Override public void handleMessage(Message msg) { 48 switch (msg.what) { 49 case MSG_WRITE: 50 saveCompatModes(); 51 break; 52 default: 53 super.handleMessage(msg); 54 break; 55 } 56 } 57 }; 58 59 public CompatModePackages(ActivityManagerService service, File systemDir) { 60 mService = service; 61 mFile = new AtomicFile(new File(systemDir, "packages-compat.xml")); 62 63 FileInputStream fis = null; 64 try { 65 fis = mFile.openRead(); 66 XmlPullParser parser = Xml.newPullParser(); 67 parser.setInput(fis, null); 68 int eventType = parser.getEventType(); 69 while (eventType != XmlPullParser.START_TAG) { 70 eventType = parser.next(); 71 } 72 String tagName = parser.getName(); 73 if ("compat-packages".equals(tagName)) { 74 eventType = parser.next(); 75 do { 76 if (eventType == XmlPullParser.START_TAG) { 77 tagName = parser.getName(); 78 if (parser.getDepth() == 2) { 79 if ("pkg".equals(tagName)) { 80 String pkg = parser.getAttributeValue(null, "name"); 81 if (pkg != null) { 82 String mode = parser.getAttributeValue(null, "mode"); 83 int modeInt = 0; 84 if (mode != null) { 85 try { 86 modeInt = Integer.parseInt(mode); 87 } catch (NumberFormatException e) { 88 } 89 } 90 mPackages.put(pkg, modeInt); 91 } 92 } 93 } 94 } 95 eventType = parser.next(); 96 } while (eventType != XmlPullParser.END_DOCUMENT); 97 } 98 } catch (XmlPullParserException e) { 99 Slog.w(TAG, "Error reading compat-packages", e); 100 } catch (java.io.IOException e) { 101 if (fis != null) Slog.w(TAG, "Error reading compat-packages", e); 102 } finally { 103 if (fis != null) { 104 try { 105 fis.close(); 106 } catch (java.io.IOException e1) { 107 } 108 } 109 } 110 } 111 112 public HashMap<String, Integer> getPackages() { 113 return mPackages; 114 } 115 116 private int getPackageFlags(String packageName) { 117 Integer flags = mPackages.get(packageName); 118 return flags != null ? flags : 0; 119 } 120 121 public void handlePackageAddedLocked(String packageName, boolean updated) { 122 ApplicationInfo ai = null; 123 try { 124 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0); 125 } catch (RemoteException e) { 126 } 127 if (ai == null) { 128 return; 129 } 130 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); 131 final boolean mayCompat = !ci.alwaysSupportsScreen() 132 && !ci.neverSupportsScreen(); 133 134 if (updated) { 135 // Update -- if the app no longer can run in compat mode, clear 136 // any current settings for it. 137 if (!mayCompat && mPackages.containsKey(packageName)) { 138 mPackages.remove(packageName); 139 mHandler.removeMessages(MSG_WRITE); 140 Message msg = mHandler.obtainMessage(MSG_WRITE); 141 mHandler.sendMessageDelayed(msg, 10000); 142 } 143 } 144 } 145 146 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { 147 CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mConfiguration.screenLayout, 148 mService.mConfiguration.smallestScreenWidthDp, 149 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0); 150 //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci); 151 return ci; 152 } 153 154 public int computeCompatModeLocked(ApplicationInfo ai) { 155 boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0; 156 CompatibilityInfo info = new CompatibilityInfo(ai, 157 mService.mConfiguration.screenLayout, 158 mService.mConfiguration.smallestScreenWidthDp, enabled); 159 if (info.alwaysSupportsScreen()) { 160 return ActivityManager.COMPAT_MODE_NEVER; 161 } 162 if (info.neverSupportsScreen()) { 163 return ActivityManager.COMPAT_MODE_ALWAYS; 164 } 165 return enabled ? ActivityManager.COMPAT_MODE_ENABLED 166 : ActivityManager.COMPAT_MODE_DISABLED; 167 } 168 169 public boolean getFrontActivityAskCompatModeLocked() { 170 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 171 if (r == null) { 172 return false; 173 } 174 return getPackageAskCompatModeLocked(r.packageName); 175 } 176 177 public boolean getPackageAskCompatModeLocked(String packageName) { 178 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0; 179 } 180 181 public void setFrontActivityAskCompatModeLocked(boolean ask) { 182 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 183 if (r != null) { 184 setPackageAskCompatModeLocked(r.packageName, ask); 185 } 186 } 187 188 public void setPackageAskCompatModeLocked(String packageName, boolean ask) { 189 int curFlags = getPackageFlags(packageName); 190 int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK); 191 if (curFlags != newFlags) { 192 if (newFlags != 0) { 193 mPackages.put(packageName, newFlags); 194 } else { 195 mPackages.remove(packageName); 196 } 197 mHandler.removeMessages(MSG_WRITE); 198 Message msg = mHandler.obtainMessage(MSG_WRITE); 199 mHandler.sendMessageDelayed(msg, 10000); 200 } 201 } 202 203 public int getFrontActivityScreenCompatModeLocked() { 204 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 205 if (r == null) { 206 return ActivityManager.COMPAT_MODE_UNKNOWN; 207 } 208 return computeCompatModeLocked(r.info.applicationInfo); 209 } 210 211 public void setFrontActivityScreenCompatModeLocked(int mode) { 212 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 213 if (r == null) { 214 Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity"); 215 return; 216 } 217 setPackageScreenCompatModeLocked(r.info.applicationInfo, mode); 218 } 219 220 public int getPackageScreenCompatModeLocked(String packageName) { 221 ApplicationInfo ai = null; 222 try { 223 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0); 224 } catch (RemoteException e) { 225 } 226 if (ai == null) { 227 return ActivityManager.COMPAT_MODE_UNKNOWN; 228 } 229 return computeCompatModeLocked(ai); 230 } 231 232 public void setPackageScreenCompatModeLocked(String packageName, int mode) { 233 ApplicationInfo ai = null; 234 try { 235 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0); 236 } catch (RemoteException e) { 237 } 238 if (ai == null) { 239 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName); 240 return; 241 } 242 setPackageScreenCompatModeLocked(ai, mode); 243 } 244 245 private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) { 246 final String packageName = ai.packageName; 247 248 int curFlags = getPackageFlags(packageName); 249 250 boolean enable; 251 switch (mode) { 252 case ActivityManager.COMPAT_MODE_DISABLED: 253 enable = false; 254 break; 255 case ActivityManager.COMPAT_MODE_ENABLED: 256 enable = true; 257 break; 258 case ActivityManager.COMPAT_MODE_TOGGLE: 259 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0; 260 break; 261 default: 262 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring"); 263 return; 264 } 265 266 int newFlags = curFlags; 267 if (enable) { 268 newFlags |= COMPAT_FLAG_ENABLED; 269 } else { 270 newFlags &= ~COMPAT_FLAG_ENABLED; 271 } 272 273 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); 274 if (ci.alwaysSupportsScreen()) { 275 Slog.w(TAG, "Ignoring compat mode change of " + packageName 276 + "; compatibility never needed"); 277 newFlags = 0; 278 } 279 if (ci.neverSupportsScreen()) { 280 Slog.w(TAG, "Ignoring compat mode change of " + packageName 281 + "; compatibility always needed"); 282 newFlags = 0; 283 } 284 285 if (newFlags != curFlags) { 286 if (newFlags != 0) { 287 mPackages.put(packageName, newFlags); 288 } else { 289 mPackages.remove(packageName); 290 } 291 292 // Need to get compatibility info in new state. 293 ci = compatibilityInfoForPackageLocked(ai); 294 295 mHandler.removeMessages(MSG_WRITE); 296 Message msg = mHandler.obtainMessage(MSG_WRITE); 297 mHandler.sendMessageDelayed(msg, 10000); 298 299 ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null); 300 301 // All activities that came from the package must be 302 // restarted as if there was a config change. 303 for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) { 304 ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i); 305 if (a.info.packageName.equals(packageName)) { 306 a.forceNewConfig = true; 307 if (starting != null && a == starting && a.visible) { 308 a.startFreezingScreenLocked(starting.app, 309 ActivityInfo.CONFIG_SCREEN_LAYOUT); 310 } 311 } 312 } 313 314 // Tell all processes that loaded this package about the change. 315 for (int i=mService.mLruProcesses.size()-1; i>=0; i--) { 316 ProcessRecord app = mService.mLruProcesses.get(i); 317 if (!app.pkgList.contains(packageName)) { 318 continue; 319 } 320 try { 321 if (app.thread != null) { 322 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " 323 + app.processName + " new compat " + ci); 324 app.thread.updatePackageCompatibilityInfo(packageName, ci); 325 } 326 } catch (Exception e) { 327 } 328 } 329 330 if (starting != null) { 331 mService.mMainStack.ensureActivityConfigurationLocked(starting, 0); 332 // And we need to make sure at this point that all other activities 333 // are made visible with the correct configuration. 334 mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0); 335 } 336 } 337 } 338 339 void saveCompatModes() { 340 HashMap<String, Integer> pkgs; 341 synchronized (mService) { 342 pkgs = new HashMap<String, Integer>(mPackages); 343 } 344 345 FileOutputStream fos = null; 346 347 try { 348 fos = mFile.startWrite(); 349 XmlSerializer out = new FastXmlSerializer(); 350 out.setOutput(fos, "utf-8"); 351 out.startDocument(null, true); 352 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 353 out.startTag(null, "compat-packages"); 354 355 final IPackageManager pm = AppGlobals.getPackageManager(); 356 final int screenLayout = mService.mConfiguration.screenLayout; 357 final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp; 358 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator(); 359 while (it.hasNext()) { 360 Map.Entry<String, Integer> entry = it.next(); 361 String pkg = entry.getKey(); 362 int mode = entry.getValue(); 363 if (mode == 0) { 364 continue; 365 } 366 ApplicationInfo ai = null; 367 try { 368 ai = pm.getApplicationInfo(pkg, 0); 369 } catch (RemoteException e) { 370 } 371 if (ai == null) { 372 continue; 373 } 374 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout, 375 smallestScreenWidthDp, false); 376 if (info.alwaysSupportsScreen()) { 377 continue; 378 } 379 if (info.neverSupportsScreen()) { 380 continue; 381 } 382 out.startTag(null, "pkg"); 383 out.attribute(null, "name", pkg); 384 out.attribute(null, "mode", Integer.toString(mode)); 385 out.endTag(null, "pkg"); 386 } 387 388 out.endTag(null, "compat-packages"); 389 out.endDocument(); 390 391 mFile.finishWrite(fos); 392 } catch (java.io.IOException e1) { 393 Slog.w(TAG, "Error writing compat packages", e1); 394 if (fos != null) { 395 mFile.failWrite(fos); 396 } 397 } 398 } 399 } 400