diff options
Diffstat (limited to 'services/java')
38 files changed, 2150 insertions, 569 deletions
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 32ac8e1..9b7be02 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -34,6 +34,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserHandle; import android.os.WorkSource; import android.text.TextUtils; import android.text.format.Time; @@ -303,7 +304,7 @@ class AlarmManagerService extends IAlarmManager.Stub { } } } - + public void removeLocked(String packageName) { removeLocked(mRtcWakeupAlarms, packageName); removeLocked(mRtcAlarms, packageName); @@ -327,6 +328,29 @@ class AlarmManagerService extends IAlarmManager.Stub { } } } + + public void removeUserLocked(int userHandle) { + removeUserLocked(mRtcWakeupAlarms, userHandle); + removeUserLocked(mRtcAlarms, userHandle); + removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle); + removeUserLocked(mElapsedRealtimeAlarms, userHandle); + } + + private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) { + if (alarmList.size() <= 0) { + return; + } + + // iterator over the list removing any it where the intent match + Iterator<Alarm> it = alarmList.iterator(); + + while (it.hasNext()) { + Alarm alarm = it.next(); + if (UserHandle.getUserId(alarm.operation.getTargetUid()) == userHandle) { + it.remove(); + } + } + } public boolean lookForPackageLocked(String packageName) { return lookForPackageLocked(mRtcWakeupAlarms, packageName) @@ -822,6 +846,7 @@ class AlarmManagerService extends IAlarmManager.Stub { // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + sdFilter.addAction(Intent.ACTION_USER_STOPPED); mContext.registerReceiver(this, sdFilter); } @@ -841,6 +866,11 @@ class AlarmManagerService extends IAlarmManager.Stub { return; } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + } else if (Intent.ACTION_USER_STOPPED.equals(action)) { + int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + if (userHandle >= 0) { + removeUserLocked(userHandle); + } } else { if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 776a1a4..8a1ac10 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -42,6 +42,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; +import android.net.CaptivePortalTracker; import android.net.ConnectivityManager; import android.net.DummyDataStateTracker; import android.net.EthernetDataTracker; @@ -166,6 +167,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private NetworkStateTracker mNetTrackers[]; + /* Handles captive portal check on a network */ + private CaptivePortalTracker mCaptivePortalTracker; + /** * The link properties that define the current links */ @@ -1363,8 +1367,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { return false; } NetworkStateTracker tracker = mNetTrackers[networkType]; + DetailedState netState = tracker.getNetworkInfo().getDetailedState(); - if (tracker == null || !tracker.getNetworkInfo().isConnected() || + if (tracker == null || (netState != DetailedState.CONNECTED && + netState != DetailedState.CAPTIVE_PORTAL_CHECK) || tracker.isTeardownRequested()) { if (VDBG) { log("requestRouteToHostAddress on down network " + @@ -1966,32 +1972,29 @@ public class ConnectivityService extends IConnectivityManager.Stub { } }; + private boolean isNewNetTypePreferredOverCurrentNetType(int type) { + if ((type != mNetworkPreference && + mNetConfigs[mActiveDefaultNetwork].priority > + mNetConfigs[type].priority) || + mNetworkPreference == mActiveDefaultNetwork) return false; + return true; + } + private void handleConnect(NetworkInfo info) { - final int type = info.getType(); + final int newNetType = info.getType(); - setupDataActivityTracking(type); + setupDataActivityTracking(newNetType); // snapshot isFailover, because sendConnectedBroadcast() resets it boolean isFailover = info.isFailover(); - final NetworkStateTracker thisNet = mNetTrackers[type]; + final NetworkStateTracker thisNet = mNetTrackers[newNetType]; final String thisIface = thisNet.getLinkProperties().getInterfaceName(); // if this is a default net and other default is running // kill the one not preferred - if (mNetConfigs[type].isDefault()) { - if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) { - if ((type != mNetworkPreference && - mNetConfigs[mActiveDefaultNetwork].priority > - mNetConfigs[type].priority) || - mNetworkPreference == mActiveDefaultNetwork) { - // don't accept this one - if (VDBG) { - log("Not broadcasting CONNECT_ACTION " + - "to torn down network " + info.getTypeName()); - } - teardown(thisNet); - return; - } else { + if (mNetConfigs[newNetType].isDefault()) { + if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) { + if (isNewNetTypePreferredOverCurrentNetType(newNetType)) { // tear down the other NetworkStateTracker otherNet = mNetTrackers[mActiveDefaultNetwork]; @@ -2004,6 +2007,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { teardown(thisNet); return; } + } else { + // don't accept this one + if (VDBG) { + log("Not broadcasting CONNECT_ACTION " + + "to torn down network " + info.getTypeName()); + } + teardown(thisNet); + return; } } synchronized (ConnectivityService.this) { @@ -2017,7 +2028,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { 1000); } } - mActiveDefaultNetwork = type; + mActiveDefaultNetwork = newNetType; // this will cause us to come up initially as unconnected and switching // to connected after our normal pause unless somebody reports us as reall // disconnected @@ -2029,19 +2040,47 @@ public class ConnectivityService extends IConnectivityManager.Stub { } thisNet.setTeardownRequested(false); updateNetworkSettings(thisNet); - handleConnectivityChange(type, false); + handleConnectivityChange(newNetType, false); sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay()); // notify battery stats service about this network if (thisIface != null) { try { - BatteryStatsService.getService().noteNetworkInterfaceType(thisIface, type); + BatteryStatsService.getService().noteNetworkInterfaceType(thisIface, newNetType); } catch (RemoteException e) { // ignored; service lives in system_server } } } + private void handleCaptivePortalTrackerCheck(NetworkInfo info) { + if (DBG) log("Captive portal check " + info); + int type = info.getType(); + final NetworkStateTracker thisNet = mNetTrackers[type]; + if (mNetConfigs[type].isDefault()) { + if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) { + if (isNewNetTypePreferredOverCurrentNetType(type)) { + if (DBG) log("Captive check on " + info.getTypeName()); + mCaptivePortalTracker = CaptivePortalTracker.detect(mContext, info, + ConnectivityService.this); + return; + } else { + if (DBG) log("Tear down low priority net " + info.getTypeName()); + teardown(thisNet); + return; + } + } + } + + thisNet.captivePortalCheckComplete(); + } + + /** @hide */ + public void captivePortalCheckComplete(NetworkInfo info) { + mNetTrackers[info.getType()].captivePortalCheckComplete(); + mCaptivePortalTracker = null; + } + /** * Setup data activity tracking for the given network interface. * @@ -2630,6 +2669,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) { handleConnectionFailure(info); + } else if (info.getDetailedState() == + DetailedState.CAPTIVE_PORTAL_CHECK) { + handleCaptivePortalTrackerCheck(info); } else if (state == NetworkInfo.State.DISCONNECTED) { handleDisconnect(info); } else if (state == NetworkInfo.State.SUSPENDED) { diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index d6fed39..9b61ec4 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -544,6 +544,11 @@ public class NotificationManagerService extends INotificationManager.Stub mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals( TelephonyManager.EXTRA_STATE_OFFHOOK)); updateNotificationPulse(); + } else if (action.equals(Intent.ACTION_USER_STOPPED)) { + int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + if (userHandle >= 0) { + cancelAllNotificationsUser(userHandle); + } } else if (action.equals(Intent.ACTION_USER_PRESENT)) { // turn off LED when user passes through lock screen mNotificationLight.turnOff(); @@ -619,6 +624,7 @@ public class NotificationManagerService extends INotificationManager.Stub filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); filter.addAction(Intent.ACTION_USER_PRESENT); + filter.addAction(Intent.ACTION_USER_STOPPED); mContext.registerReceiver(mIntentReceiver, filter); IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -1249,6 +1255,29 @@ public class NotificationManagerService extends INotificationManager.Stub } } + /** + * Cancels all notifications from a given user. + */ + boolean cancelAllNotificationsUser(int userHandle) { + synchronized (mNotificationList) { + final int N = mNotificationList.size(); + boolean canceledSomething = false; + for (int i = N-1; i >= 0; --i) { + NotificationRecord r = mNotificationList.get(i); + if (UserHandle.getUserId(r.uid) != userHandle) { + continue; + } + canceledSomething = true; + mNotificationList.remove(i); + cancelNotificationLocked(r, false); + } + if (canceledSomething) { + updateLightsLocked(); + } + return canceledSomething; + } + } + @Deprecated public void cancelNotification(String pkg, int id) { cancelNotificationWithTag(pkg, null /* tag */, id); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7097891..48b1215 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -28,6 +28,8 @@ import android.content.pm.IPackageManager; import android.content.res.Configuration; import android.media.AudioService; import android.net.wifi.p2p.WifiP2pService; +import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; import android.os.RemoteException; import android.os.SchedulingPolicyService; @@ -147,7 +149,52 @@ class ServerThread extends Thread { CommonTimeManagementService commonTimeMgmtService = null; InputManagerService inputManager = null; + // Create a shared handler thread for UI within the system server. + // This thread is used by at least the following components: + // - WindowManagerPolicy + // - KeyguardViewManager + // - DisplayManagerService + HandlerThread uiHandlerThread = new HandlerThread("UI"); + uiHandlerThread.start(); + Handler uiHandler = new Handler(uiHandlerThread.getLooper()); + uiHandler.post(new Runnable() { + @Override + public void run() { + //Looper.myLooper().setMessageLogging(new LogPrinter( + // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM)); + android.os.Process.setThreadPriority( + android.os.Process.THREAD_PRIORITY_FOREGROUND); + android.os.Process.setCanSelfBackground(false); + + // For debug builds, log event loop stalls to dropbox for analysis. + if (StrictMode.conditionallyEnableDebugLogging()) { + Slog.i(TAG, "Enabled StrictMode logging for UI Looper"); + } + } + }); + + // Create a handler thread just for the window manager to enjoy. + HandlerThread wmHandlerThread = new HandlerThread("WindowManager"); + wmHandlerThread.start(); + Handler wmHandler = new Handler(wmHandlerThread.getLooper()); + wmHandler.post(new Runnable() { + @Override + public void run() { + //Looper.myLooper().setMessageLogging(new LogPrinter( + // android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM)); + android.os.Process.setThreadPriority( + android.os.Process.THREAD_PRIORITY_DISPLAY); + android.os.Process.setCanSelfBackground(false); + + // For debug builds, log event loop stalls to dropbox for analysis. + if (StrictMode.conditionallyEnableDebugLogging()) { + Slog.i(TAG, "Enabled StrictMode logging for UI Looper"); + } + } + }); + // Critical services... + boolean onlyCore = false; try { Slog.i(TAG, "Entropy Mixer"); ServiceManager.addService("entropy", new EntropyMixer()); @@ -160,7 +207,7 @@ class ServerThread extends Thread { context = ActivityManagerService.main(factoryTest); Slog.i(TAG, "Display Manager"); - display = new DisplayManagerService(context); + display = new DisplayManagerService(context, uiHandler); ServiceManager.addService(Context.DISPLAY_SERVICE, display, true); Slog.i(TAG, "Telephony Registry"); @@ -172,10 +219,14 @@ class ServerThread extends Thread { AttributeCache.init(context); + if (!display.waitForDefaultDisplay()) { + reportWtf("Timeout waiting for default display to be initialized.", + new Throwable()); + } + Slog.i(TAG, "Package Manager"); // Only run "core" apps if we're encrypting the device. String cryptState = SystemProperties.get("vold.decrypt"); - boolean onlyCore = false; if (ENCRYPTING_STATE.equals(cryptState)) { Slog.w(TAG, "Detected encryption in progress - only parsing core apps"); onlyCore = true; @@ -244,6 +295,7 @@ class ServerThread extends Thread { Slog.i(TAG, "Window Manager"); wm = WindowManagerService.main(context, power, display, + uiHandler, wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL, !firstBoot, onlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); @@ -753,6 +805,12 @@ class ServerThread extends Thread { reportWtf("making Package Manager Service ready", e); } + try { + display.systemReady(safeMode, onlyCore); + } catch (Throwable e) { + reportWtf("making Display Manager Service ready", e); + } + // These are needed to propagate to the runnable below. final Context contextF = context; final BatteryService batteryF = battery; diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index afd7d0e..7ed698b 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -19,6 +19,7 @@ package com.android.server; import static android.os.FileObserver.*; import static android.os.ParcelFileDescriptor.*; +import android.app.AppGlobals; import android.app.IWallpaperManager; import android.app.IWallpaperManagerCallback; import android.app.PendingIntent; @@ -31,6 +32,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; @@ -146,6 +148,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { final Context mContext; final IWindowManager mIWindowManager; + final IPackageManager mIPackageManager; final MyPackageMonitor mMonitor; WallpaperData mLastWallpaper; @@ -389,6 +392,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { mContext = context; mIWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService(Context.WINDOW_SERVICE)); + mIPackageManager = AppGlobals.getPackageManager(); mMonitor = new MyPackageMonitor(); mMonitor.register(context, null, true); WALLPAPER_BASE_DIR.mkdirs(); @@ -710,8 +714,9 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (DEBUG) Slog.v(TAG, "Using image wallpaper"); } } - ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName, - PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS); + int serviceUserId = wallpaper.userId; + ServiceInfo si = mIPackageManager.getServiceInfo(componentName, + PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId); if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) { String msg = "Selected service does not require " + android.Manifest.permission.BIND_WALLPAPER @@ -728,8 +733,10 @@ class WallpaperManagerService extends IWallpaperManager.Stub { Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); if (componentName != null && !componentName.equals(wallpaper.imageWallpaperComponent)) { // Make sure the selected service is actually a wallpaper service. - List<ResolveInfo> ris = mContext.getPackageManager() - .queryIntentServices(intent, PackageManager.GET_META_DATA); + List<ResolveInfo> ris = + mIPackageManager.queryIntentServices(intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + PackageManager.GET_META_DATA, serviceUserId); for (int i=0; i<ris.size(); i++) { ServiceInfo rsi = ris.get(i).serviceInfo; if (rsi.name.equals(si.name) && @@ -767,7 +774,6 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (DEBUG) Slog.v(TAG, "Binding to:" + componentName); WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper); intent.setComponent(componentName); - int serviceUserId = wallpaper.userId; intent.putExtra(Intent.EXTRA_CLIENT_LABEL, com.android.internal.R.string.wallpaper_binding_label); intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( @@ -800,8 +806,8 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } } catch (RemoteException e) { } - } catch (PackageManager.NameNotFoundException e) { - String msg = "Unknown component " + componentName; + } catch (RemoteException e) { + String msg = "Remote exception for " + componentName + "\n" + e; if (fromUser) { throw new IllegalArgumentException(msg); } diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index 9edfad6..2c5038e 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -26,6 +26,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.Build; import android.os.Debug; import android.os.Handler; import android.os.Message; @@ -39,6 +40,8 @@ import android.util.Log; import android.util.Slog; import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; @@ -429,11 +432,10 @@ public class Watchdog extends Thread { } // If we got here, that means that the system is most likely hung. - // First collect stack traces from all threads of the system process. - // Then kill this process so that the system will restart. final String name = (mCurrentMonitor != null) ? mCurrentMonitor.getClass().getName() : "null"; + Slog.w(TAG, "WATCHDOG PROBLEM IN SYSTEM SERVER: " + name); EventLog.writeEvent(EventLogTags.WATCHDOG, name); ArrayList<Integer> pids = new ArrayList<Integer>(); @@ -468,11 +470,15 @@ public class Watchdog extends Thread { dropboxThread.join(2000); // wait up to 2 seconds for it to return. } catch (InterruptedException ignored) {} - // Only kill the process if the debugger is not attached. + // Only kill/crash the process if the debugger is not attached. if (!Debug.isDebuggerConnected()) { Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name); - Process.killProcess(Process.myPid()); - System.exit(10); + if (!Build.TYPE.equals("user")) { + forceCrashDump(); + } else { + Process.killProcess(Process.myPid()); + System.exit(10); + } } else { Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process"); } @@ -481,6 +487,50 @@ public class Watchdog extends Thread { } } + private void forceCrashDump() { + /* Sync file system to flash the data which is written just before the + * crash. + */ + java.lang.Process p = null; + try { + p = Runtime.getRuntime().exec("sync"); + if (p != null) { + // It is not necessary to check the exit code, here. + // 'sync' command always succeeds, and this function returns 0. + p.waitFor(); + } else { + Slog.e(TAG, "Failed to execute 'sync' command. (no process handle)"); + } + } catch (Exception e) { + // This code is an emergency path to crash MUT. The system already + // caused fatal error, and then calls this function to create a + // crash dump. This function must run the code below to force a + // crash, even if the sync command failed. + // Therefore, ignore all exceptions, here. + Slog.e(TAG, "Failed to execute 'sync' command prior to forcing crash: " + e); + } finally { + if (p != null) { + p.destroy(); + } + } + + FileWriter out = null; + try { + out = new FileWriter("/proc/sysrq-trigger"); + out.write("c"); + } catch (IOException e) { + Slog.e(TAG, "Failed to write to sysrq-trigger while triggering crash: " + e); + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + Slog.e(TAG, "Failed to close sysrq-trigger while triggering crash: " + e); + } + } + } + } + private File dumpKernelStackTraces() { String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null); if (tracesPath == null || tracesPath.length() == 0) { diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index f483576..6bc5e10 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -240,7 +240,7 @@ public class WifiService extends IWifiManager.Stub { switch (msg.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - Slog.d(TAG, "New client listening to asynchronous messages"); + if (DBG) Slog.d(TAG, "New client listening to asynchronous messages"); mClients.add((AsyncChannel) msg.obj); } else { Slog.e(TAG, "Client connection failure, error=" + msg.arg1); @@ -249,9 +249,9 @@ public class WifiService extends IWifiManager.Stub { } case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { - Slog.d(TAG, "Send failed, client connection lost"); + if (DBG) Slog.d(TAG, "Send failed, client connection lost"); } else { - Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); + if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); } mClients.remove((AsyncChannel) msg.obj); break; @@ -412,6 +412,7 @@ public class WifiService extends IWifiManager.Stub { switch(mNetworkInfo.getDetailedState()) { case CONNECTED: case DISCONNECTED: + case CAPTIVE_PORTAL_CHECK: evaluateTrafficStatsPolling(); resetNotification(); break; @@ -606,6 +607,12 @@ public class WifiService extends IWifiManager.Stub { "WifiService"); } + private void enforceConnectivityInternalPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CONNECTIVITY_INTERNAL, + "ConnectivityService"); + } + /** * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} * @param enable {@code true} to enable, {@code false} to disable. @@ -910,7 +917,7 @@ public class WifiService extends IWifiManager.Stub { * */ public void startWifi() { - enforceChangePermission(); + enforceConnectivityInternalPermission(); /* TODO: may be add permissions for access only to connectivity service * TODO: if a start issued, keep wifi alive until a stop issued irrespective * of WifiLock & device idle status unless wifi enabled status is toggled @@ -920,20 +927,24 @@ public class WifiService extends IWifiManager.Stub { mWifiStateMachine.reconnectCommand(); } + public void captivePortalCheckComplete() { + enforceConnectivityInternalPermission(); + mWifiStateMachine.captivePortalCheckComplete(); + } + /** * see {@link android.net.wifi.WifiManager#stopWifi} * */ public void stopWifi() { - enforceChangePermission(); - /* TODO: may be add permissions for access only to connectivity service + enforceConnectivityInternalPermission(); + /* * TODO: if a stop is issued, wifi is brought up only by startWifi * unless wifi enabled status is toggled */ mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode); } - /** * see {@link android.net.wifi.WifiManager#addToBlacklist} * diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index ca7faa2..b0dfa80 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -989,6 +989,17 @@ public class ActiveServices { // restarting state. mRestartingServices.remove(r); + // Make sure that the user who owns this service is started. If not, + // we don't want to allow it to run. + if (mAm.mStartedUsers.get(r.userId) == null) { + Slog.w(TAG, "Unable to launch app " + + r.appInfo.packageName + "/" + + r.appInfo.uid + " for service " + + r.intent.getIntent() + ": user " + r.userId + " is stopped"); + bringDownServiceLocked(r, true); + return false; + } + // Service is now being launched, its package can't be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( @@ -1509,7 +1520,7 @@ public class ActiveServices { boolean didSomething = false; ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); for (ServiceRecord service : mServiceMap.getAllServices(userId)) { - if (service.packageName.equals(name) + if ((name == null || service.packageName.equals(name)) && (service.app == null || evenPersistent || !service.app.persistent)) { if (!doit) { return true; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 2b4f8b1..a061d58 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -46,6 +46,7 @@ import android.app.IInstrumentationWatcher; import android.app.INotificationManager; import android.app.IProcessObserver; import android.app.IServiceConnection; +import android.app.IStopUserCallback; import android.app.IThumbnailReceiver; import android.app.Instrumentation; import android.app.Notification; @@ -428,6 +429,11 @@ public final class ActivityManagerService extends ActivityManagerNative long mPreviousProcessVisibleTime; /** + * Which uses have been started, so are allowed to run code. + */ + final SparseArray<UserStartedState> mStartedUsers = new SparseArray<UserStartedState>(); + + /** * Packages that the user has asked to have run in screen size * compatibility mode instead of filling the screen. */ @@ -791,7 +797,6 @@ public final class ActivityManagerService extends ActivityManagerNative static ActivityThread mSystemThread; private int mCurrentUserId; - private SparseIntArray mLoggedInUsers = new SparseIntArray(5); private UserManager mUserManager; private final class AppDeathRecipient implements IBinder.DeathRecipient { @@ -1506,6 +1511,9 @@ public final class ActivityManagerService extends ActivityManagerNative systemDir, "usagestats").toString()); mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0")); + // User 0 is the first and only user that runs at boot. + mStartedUsers.put(0, new UserStartedState(new UserHandle(0), true)); + GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", ConfigurationInfo.GL_ES_VERSION_UNDEFINED); @@ -1983,13 +1991,19 @@ public final class ActivityManagerService extends ActivityManagerNative try { final PackageManager pm = mContext.getPackageManager(); gids = pm.getPackageGids(app.info.packageName); + + if (Environment.isExternalStorageEmulated()) { + if (pm.checkPermission( + android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE, + app.info.packageName) == PERMISSION_GRANTED) { + mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL; + } else { + mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER; + } + } } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Unable to retrieve gids", e); } - - if (Environment.isExternalStorageEmulated()) { - mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER; - } } if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) { if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL @@ -2095,7 +2109,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - boolean startHomeActivityLocked(int userId) { + boolean startHomeActivityLocked(int userId, UserStartedState startingUser) { if (mHeadless) { // Added because none of the other calls to ensureBootCompleted seem to fire // when running headless. @@ -2135,6 +2149,9 @@ public final class ActivityManagerService extends ActivityManagerNative null, null, 0, 0, 0, 0, null, false, null); } } + if (startingUser != null) { + mMainStack.addStartingUserLocked(startingUser); + } return true; } @@ -3454,7 +3471,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "Invalid packageName: " + packageName); return; } - killPackageProcessesLocked(packageName, pkgUid, + killPackageProcessesLocked(packageName, pkgUid, -1, ProcessList.SERVICE_ADJ, false, true, true, false, "kill background"); } } finally { @@ -3650,7 +3667,8 @@ public final class ActivityManagerService extends ActivityManagerNative } private void forceStopPackageLocked(final String packageName, int uid) { - forceStopPackageLocked(packageName, uid, false, false, true, false, UserHandle.getUserId(uid)); + forceStopPackageLocked(packageName, uid, false, false, true, false, + UserHandle.getUserId(uid)); Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, Uri.fromParts("package", packageName, null)); if (!mProcessesReady) { @@ -3662,16 +3680,27 @@ public final class ActivityManagerService extends ActivityManagerNative false, false, MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid)); } - + + private void forceStopUserLocked(int userId) { + forceStopPackageLocked(null, -1, false, false, true, false, userId); + Intent intent = new Intent(Intent.ACTION_USER_STOPPED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, + false, false, + MY_PID, Process.SYSTEM_UID, userId); + } + private final boolean killPackageProcessesLocked(String packageName, int uid, - int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit, - boolean evenPersistent, String reason) { + int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart, + boolean doit, boolean evenPersistent, String reason) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); // Remove all processes this package may have touched: all with the // same UID (except for the system or root user), and all whose name // matches the package name. - final String procNamePrefix = packageName + ":"; + final String procNamePrefix = packageName != null ? (packageName + ":") : null; for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { final int NA = apps.size(); for (int ia=0; ia<NA; ia++) { @@ -3684,6 +3713,18 @@ public final class ActivityManagerService extends ActivityManagerNative if (doit) { procs.add(app); } + // If no package is specified, we call all processes under the + // give user id. + } else if (packageName == null) { + if (app.userId == userId) { + if (app.setAdj >= minOomAdj) { + if (!doit) { + return true; + } + app.removed = true; + procs.add(app); + } + } // If uid is specified and the uid and process name match // Or, the uid is not specified and the process name matches } else if (((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid) @@ -3714,7 +3755,7 @@ public final class ActivityManagerService extends ActivityManagerNative int i; int N; - if (uid < 0) { + if (uid < 0 && name != null) { try { uid = AppGlobals.getPackageManager().getPackageUid(name, userId); } catch (RemoteException e) { @@ -3722,24 +3763,45 @@ public final class ActivityManagerService extends ActivityManagerNative } if (doit) { - Slog.i(TAG, "Force stopping package " + name + " uid=" + uid); + if (name != null) { + Slog.i(TAG, "Force stopping package " + name + " uid=" + uid); + } else { + Slog.i(TAG, "Force stopping user " + userId); + } Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator(); while (badApps.hasNext()) { SparseArray<Long> ba = badApps.next(); - if (ba.get(uid) != null) { + for (i=ba.size()-1; i>=0; i--) { + boolean remove = false; + final int entUid = ba.keyAt(i); + if (name != null) { + if (entUid == uid) { + remove = true; + } + } else if (UserHandle.getUserId(entUid) == userId) { + remove = true; + } + if (remove) { + ba.removeAt(i); + } + } + if (ba.size() == 0) { badApps.remove(); } } } - - boolean didSomething = killPackageProcessesLocked(name, uid, -100, - callerWillRestart, false, doit, evenPersistent, "force stop"); + + boolean didSomething = killPackageProcessesLocked(name, uid, + name == null ? userId : -1 , -100, callerWillRestart, false, + doit, evenPersistent, + name == null ? ("force stop user " + userId) : ("force stop " + name)); TaskRecord lastTask = null; for (i=0; i<mMainStack.mHistory.size(); i++) { ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); - final boolean samePackage = r.packageName.equals(name); + final boolean samePackage = r.packageName.equals(name) + || (name == null && r.userId == userId); if (r.userId == userId && (samePackage || r.task == lastTask) && (r.app == null || evenPersistent || !r.app.persistent)) { @@ -3776,7 +3838,7 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>(); for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(userId).values()) { - if (provider.info.packageName.equals(name) + if ((name == null || provider.info.packageName.equals(name)) && (provider.proc == null || evenPersistent || !provider.proc.persistent)) { if (!doit) { return true; @@ -3792,7 +3854,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (doit) { - if (purgeCache) { + if (purgeCache && name != null) { AttributeCache ac = AttributeCache.instance(); if (ac != null) { ac.removePackage(name); @@ -4197,15 +4259,6 @@ public final class ActivityManagerService extends ActivityManagerNative } }, pkgFilter); - IntentFilter userFilter = new IntentFilter(); - userFilter.addAction(Intent.ACTION_USER_REMOVED); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - onUserRemoved(intent); - } - }, userFilter); - synchronized (this) { // Ensure that any processes we had put on hold are now started // up. @@ -4227,13 +4280,17 @@ public final class ActivityManagerService extends ActivityManagerNative // Tell anyone interested that we are done booting! SystemProperties.set("sys.boot_completed", "1"); SystemProperties.set("dev.bootcomplete", "1"); - List<UserInfo> users = getUserManager().getUsers(); - for (UserInfo user : users) { - broadcastIntentLocked(null, null, - new Intent(Intent.ACTION_BOOT_COMPLETED, null), - null, null, 0, null, null, - android.Manifest.permission.RECEIVE_BOOT_COMPLETED, - false, false, MY_PID, Process.SYSTEM_UID, user.id); + for (int i=0; i<mStartedUsers.size(); i++) { + UserStartedState uss = mStartedUsers.valueAt(i); + if (uss.mState == UserStartedState.STATE_BOOTING) { + uss.mState = UserStartedState.STATE_RUNNING; + broadcastIntentLocked(null, null, + new Intent(Intent.ACTION_BOOT_COMPLETED, null), + null, null, 0, null, null, + android.Manifest.permission.RECEIVE_BOOT_COMPLETED, + false, false, MY_PID, Process.SYSTEM_UID, + mStartedUsers.keyAt(i)); + } } } } @@ -4430,7 +4487,8 @@ public final class ActivityManagerService extends ActivityManagerNative PendingIntentRecord.Key key = new PendingIntentRecord.Key( type, packageName, activity, resultWho, - requestCode, intents, resolvedTypes, flags, options); + requestCode, intents, resolvedTypes, flags, options, + UserHandle.getUserId(callingUid)); WeakReference<PendingIntentRecord> ref; ref = mIntentSenderRecords.get(key); PendingIntentRecord rec = ref != null ? ref.get() : null; @@ -6296,6 +6354,16 @@ public final class ActivityManagerService extends ActivityManagerNative "Attempt to launch content provider before system ready"); } + // Make sure that the user who owns this provider is started. If not, + // we don't want to allow it to run. + if (mStartedUsers.get(userId) == null) { + Slog.w(TAG, "Unable to launch app " + + cpi.applicationInfo.packageName + "/" + + cpi.applicationInfo.uid + " for provider " + + name + ": user " + userId + " is stopped"); + return null; + } + ComponentName comp = new ComponentName(cpi.packageName, cpi.name); cpr = mProviderMap.getProviderByClass(comp, userId); final boolean firstClass = cpr == null; @@ -9047,6 +9115,13 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(); + pw.println(" mStartedUsers:"); + for (int i=0; i<mStartedUsers.size(); i++) { + UserStartedState uss = mStartedUsers.valueAt(i); + pw.print(" User #"); pw.print(uss.mHandle.getIdentifier()); + pw.println(":"); + uss.dump(" ", pw); + } pw.println(" mHomeProcess: " + mHomeProcess); pw.println(" mPreviousProcess: " + mPreviousProcess); if (dumpAll) { @@ -9404,9 +9479,15 @@ public final class ActivityManagerService extends ActivityManagerNative boolean dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; - + boolean onlyHistory = false; + + if ("history".equals(dumpPackage)) { + onlyHistory = true; + dumpPackage = null; + } + pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)"); - if (dumpAll) { + if (!onlyHistory && dumpAll) { if (mRegisteredReceivers.size() > 0) { boolean printed = false; Iterator it = mRegisteredReceivers.values().iterator(); @@ -9439,7 +9520,7 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; - if (mStickyBroadcasts != null && dumpPackage == null) { + if (!onlyHistory && mStickyBroadcasts != null && dumpPackage == null) { if (needSep) { pw.println(); } @@ -9471,7 +9552,7 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; } - if (dumpAll) { + if (!onlyHistory && dumpAll) { pw.println(); for (BroadcastQueue queue : mBroadcastQueues) { pw.println(" mBroadcastsScheduled [" + queue.mQueueName + "]=" @@ -10667,7 +10748,25 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("File descriptors passed in Intent"); } - checkValidCaller(Binder.getCallingUid(), userId); + if (userId != UserHandle.getCallingUserId()) { + // Requesting a different user, make sure that they have permission + if (checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Binder.getCallingPid(), Binder.getCallingUid(), -1, true) + == PackageManager.PERMISSION_GRANTED) { + // Translate to the current user id, if caller wasn't aware + if (userId == UserHandle.USER_CURRENT) { + userId = mCurrentUserId; + } + } else { + String msg = "Permission Denial: Request to bindService as user " + userId + + " but is calling from user " + UserHandle.getCallingUserId() + + "; this requires " + + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } synchronized(this) { return mServices.bindServiceLocked(caller, token, service, resolvedType, @@ -10982,7 +11081,7 @@ public final class ActivityManagerService extends ActivityManagerNative BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, null, null, -1, -1, null, receivers, null, 0, null, null, - false, true, true, false); + false, true, true, -1); queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } @@ -11075,33 +11174,39 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!"); } - boolean onlySendToCaller = false; - // If the caller is trying to send this broadcast to a different // user, verify that is allowed. if (UserHandle.getUserId(callingUid) != userId) { if (checkComponentPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - callingPid, callingUid, -1, true) - != PackageManager.PERMISSION_GRANTED) { - if (checkComponentPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS, - callingPid, callingUid, -1, true) - == PackageManager.PERMISSION_GRANTED) { - onlySendToCaller = true; - } else { - String msg = "Permission Denial: " + intent.getAction() - + " broadcast from " + callerPackage - + " asks to send as user " + userId - + " but is calling from user " + UserHandle.getUserId(callingUid) - + "; this requires " - + android.Manifest.permission.INTERACT_ACROSS_USERS; - Slog.w(TAG, msg); - throw new SecurityException(msg); + android.Manifest.permission.INTERACT_ACROSS_USERS, + callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED + && checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + callingPid, callingUid, -1, true) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: " + intent.getAction() + + " broadcast from " + callerPackage + + " asks to send as user " + userId + + " but is calling from user " + UserHandle.getUserId(callingUid) + + "; this requires " + + android.Manifest.permission.INTERACT_ACROSS_USERS; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } else { + if (userId == UserHandle.USER_CURRENT) { + userId = mCurrentUserId; } } } + // Make sure that the user who is receiving this broadcast is started + // If not, we will just skip it. + if (mStartedUsers.get(userId) == null) { + Slog.w(TAG, "Skipping broadcast of " + intent + + ": user " + userId + " is stopped"); + return ActivityManager.BROADCAST_SUCCESS; + } + // Handle special intents: if this broadcast is from the package // manager about a package being removed, we need to remove all of // its activities from the history stack. @@ -11288,7 +11393,7 @@ public final class ActivityManagerService extends ActivityManagerNative BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, registeredReceivers, resultTo, resultCode, resultData, map, - ordered, sticky, false, onlySendToCaller); + ordered, sticky, false, userId); if (DEBUG_BROADCAST) Slog.v( TAG, "Enqueueing parallel broadcast " + r); final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r); @@ -11378,7 +11483,7 @@ public final class ActivityManagerService extends ActivityManagerNative BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, receivers, resultTo, resultCode, resultData, map, ordered, - sticky, false, onlySendToCaller); + sticky, false, userId); if (DEBUG_BROADCAST) Slog.v( TAG, "Enqueueing ordered broadcast " + r + ": prev had " + queue.mOrderedBroadcasts.size()); @@ -11644,7 +11749,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.instrumentationProfileFile = null; app.instrumentationArguments = null; - forceStopPackageLocked(app.processName, -1, false, false, true, true, app.userId); + forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, app.userId); } public void finishInstrumentation(IApplicationThread target, @@ -13486,75 +13591,174 @@ public final class ActivityManagerService extends ActivityManagerNative // Multi-user methods + @Override public boolean switchUser(int userId) { - final int callingUid = Binder.getCallingUid(); - if (callingUid != 0 && callingUid != Process.myUid()) { - Slog.e(TAG, "Trying to switch user from unauthorized app"); - return false; + if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: switchUser() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); } - if (mCurrentUserId == userId) - return true; - synchronized (this) { - // Check if user is already logged in, otherwise check if user exists first before - // adding to the list of logged in users. - if (mLoggedInUsers.indexOfKey(userId) < 0) { - if (!userExists(userId)) { - return false; - } - mLoggedInUsers.append(userId, userId); + if (mCurrentUserId == userId) { + return true; + } + + // If the user we are switching to is not currently started, then + // we need to start it now. + if (mStartedUsers.get(userId) == null) { + mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false)); } mCurrentUserId = userId; boolean haveActivities = mMainStack.switchUser(userId); if (!haveActivities) { - startHomeActivityLocked(userId); + startHomeActivityLocked(userId, mStartedUsers.get(userId)); } - } - // Inform of user switch - Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS); + long ident = Binder.clearCallingIdentity(); + try { + // Inform of user switch + Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS); + } finally { + Binder.restoreCallingIdentity(ident); + } return true; } - @Override - public UserInfo getCurrentUser() throws RemoteException { - final int callingUid = Binder.getCallingUid(); - if (callingUid != 0 && callingUid != Process.myUid()) { - Slog.e(TAG, "Trying to get user from unauthorized app"); - return null; + void finishUserSwitch(UserStartedState uss) { + synchronized (this) { + if (uss.mState == UserStartedState.STATE_BOOTING + && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) { + uss.mState = UserStartedState.STATE_RUNNING; + broadcastIntentLocked(null, null, + new Intent(Intent.ACTION_BOOT_COMPLETED, null), + null, null, 0, null, null, + android.Manifest.permission.RECEIVE_BOOT_COMPLETED, + false, false, MY_PID, Process.SYSTEM_UID, uss.mHandle.getIdentifier()); + } } - return getUserManager().getUserInfo(mCurrentUserId); } - private void onUserRemoved(Intent intent) { - int extraUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (extraUserId < 1) return; - - // Kill all the processes for the user - ArrayList<Pair<String, Integer>> pkgAndUids = new ArrayList<Pair<String,Integer>>(); + @Override + public int stopUser(final int userId, final IStopUserCallback callback) { + if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: switchUser() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + if (userId <= 0) { + throw new IllegalArgumentException("Can't stop primary user " + userId); + } synchronized (this) { - HashMap<String,SparseArray<ProcessRecord>> map = mProcessNames.getMap(); - for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) { - SparseArray<ProcessRecord> uids = uidMap.getValue(); - for (int i = 0; i < uids.size(); i++) { - if (UserHandle.getUserId(uids.keyAt(i)) == extraUserId) { - pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i))); - } + if (mCurrentUserId == userId) { + return ActivityManager.USER_OP_IS_CURRENT; + } + + final UserStartedState uss = mStartedUsers.get(userId); + if (uss == null) { + // User is not started, nothing to do... but we do need to + // callback if requested. + if (callback != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + try { + callback.userStopped(userId); + } catch (RemoteException e) { + } + } + }); } + return ActivityManager.USER_OP_SUCCESS; + } + + if (callback != null) { + uss.mStopCallbacks.add(callback); + } + + if (uss.mState != UserStartedState.STATE_STOPPING) { + uss.mState = UserStartedState.STATE_STOPPING; + + long ident = Binder.clearCallingIdentity(); + try { + // Inform of user switch + Intent intent = new Intent(Intent.ACTION_SHUTDOWN); + final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, String data, + Bundle extras, boolean ordered, boolean sticky) { + finishUserStop(uss); + } + }; + broadcastIntentLocked(null, null, intent, + null, resultReceiver, 0, null, null, null, + true, false, MY_PID, Process.SYSTEM_UID, userId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + return ActivityManager.USER_OP_SUCCESS; + } + + void finishUserStop(UserStartedState uss) { + final int userId = uss.mHandle.getIdentifier(); + boolean stopped; + ArrayList<IStopUserCallback> callbacks; + synchronized (this) { + callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks); + if (uss.mState != UserStartedState.STATE_STOPPING + || mStartedUsers.get(userId) != uss) { + stopped = false; + } else { + stopped = true; + // User can no longer run. + mStartedUsers.remove(userId); + + // Clean up all state and processes associated with the user. + // Kill all the processes for the user. + forceStopUserLocked(userId); } + } - for (Pair<String,Integer> pkgAndUid : pkgAndUids) { - forceStopPackageLocked(pkgAndUid.first, pkgAndUid.second, - false, false, true, true, extraUserId); + for (int i=0; i<callbacks.size(); i++) { + try { + if (stopped) callbacks.get(i).userStopped(userId); + else callbacks.get(i).userStopAborted(userId); + } catch (RemoteException e) { } } } + @Override + public UserInfo getCurrentUser() { + if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: getCurrentUser() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + synchronized (this) { + return getUserManager().getUserInfo(mCurrentUserId); + } + } + private boolean userExists(int userId) { UserInfo user = getUserManager().getUserInfo(userId); return user != null; diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index ccea41a..a389edc 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -211,7 +211,10 @@ final class ActivityStack { */ final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible = new ArrayList<IActivityManager.WaitResult>(); - + + final ArrayList<UserStartedState> mStartingUsers + = new ArrayList<UserStartedState>(); + /** * Set when the system is going to sleep, until we have * successfully paused the current activity and released our wake lock. @@ -1397,7 +1400,7 @@ final class ActivityStack { // Launcher... if (mMainStack) { ActivityOptions.abort(options); - return mService.startHomeActivityLocked(0); + return mService.startHomeActivityLocked(0, null); } } @@ -1427,7 +1430,16 @@ final class ActivityStack { ActivityOptions.abort(options); return false; } - + + // Make sure that the user who owns this activity is started. If not, + // we will just leave it as is because someone should be bringing + // another user's activities to the top of the stack. + if (mService.mStartedUsers.get(next.userId) == null) { + Slog.w(TAG, "Skipping resume of top activity " + next + + ": user " + next.userId + " is stopped"); + return false; + } + // The activity may be waiting for stop, but that is no longer // appropriate for it. mStoppingActivities.remove(next); @@ -1494,7 +1506,7 @@ final class ActivityStack { Slog.d(TAG, "no-history finish of " + last + " on new resume"); } requestFinishActivityLocked(last.appToken, Activity.RESULT_CANCELED, null, - "no-history"); + "no-history"); } } @@ -3414,6 +3426,7 @@ final class ActivityStack { ArrayList<ActivityRecord> stops = null; ArrayList<ActivityRecord> finishes = null; ArrayList<ActivityRecord> thumbnails = null; + ArrayList<UserStartedState> startingUsers = null; int NS = 0; int NF = 0; int NT = 0; @@ -3495,6 +3508,10 @@ final class ActivityStack { booting = mService.mBooting; mService.mBooting = false; } + if (mStartingUsers.size() > 0) { + startingUsers = new ArrayList<UserStartedState>(mStartingUsers); + mStartingUsers.clear(); + } } int i; @@ -3539,6 +3556,10 @@ final class ActivityStack { if (booting) { mService.finishBooting(); + } else if (startingUsers != null) { + for (i=0; i<startingUsers.size(); i++) { + mService.finishUserSwitch(startingUsers.get(i)); + } } mService.trimApplications(); @@ -3556,6 +3577,10 @@ final class ActivityStack { return res; } + final void addStartingUserLocked(UserStartedState uss) { + mStartingUsers.add(uss); + } + /** * @return Returns true if the activity is being finished, false if for * some reason it is being left as-is. diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index 7873dd8..34dec3a 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -372,17 +372,7 @@ public class BroadcastQueue { private final void deliverToRegisteredReceiverLocked(BroadcastRecord r, BroadcastFilter filter, boolean ordered) { boolean skip = false; - if (r.onlySendToCaller) { - if (!UserHandle.isSameApp(r.callingUid, filter.owningUid)) { - Slog.w(TAG, "Permission Denial: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" - + r.callingPid + ", uid=" + r.callingUid + ")" - + " not allowed to go to different app " + filter.owningUid); - skip = true; - } - } - if (!skip && filter.requiredPermission != null) { + if (filter.requiredPermission != null) { int perm = mService.checkComponentPermission(filter.requiredPermission, r.callingPid, r.callingUid, -1, true); if (perm != PackageManager.PERMISSION_GRANTED) { @@ -667,18 +657,6 @@ public class BroadcastQueue { info.activityInfo.name); boolean skip = false; - if (r.onlySendToCaller) { - if (!UserHandle.isSameApp(r.callingUid, info.activityInfo.applicationInfo.uid)) { - Slog.w(TAG, "Permission Denial: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" - + r.callingPid + ", uid=" + r.callingUid + ")" - + " to " + component.flattenToShortString() - + " not allowed to go to different app " - + info.activityInfo.applicationInfo.uid); - skip = true; - } - } int perm = mService.checkComponentPermission(info.activityInfo.permission, r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid, info.activityInfo.exported); diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index 799b609..ca6d5f7 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -44,7 +44,7 @@ class BroadcastRecord extends Binder { final boolean ordered; // serialize the send to receivers? final boolean sticky; // originated from existing sticky data? final boolean initialSticky; // initial broadcast from register to sticky? - final boolean onlySendToCaller; // only allow receipt by sender's components? + final int userId; // user id this broadcast was for final String requiredPermission; // a permission the caller has required final List receivers; // contains BroadcastFilter and ResolveInfo IIntentReceiver resultTo; // who receives final result if non-null @@ -80,7 +80,7 @@ class BroadcastRecord extends Binder { void dump(PrintWriter pw, String prefix) { final long now = SystemClock.uptimeMillis(); - pw.print(prefix); pw.println(this); + pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId); pw.print(prefix); pw.println(intent); if (sticky) { Bundle bundle = intent.getExtras(); @@ -141,14 +141,15 @@ class BroadcastRecord extends Binder { pw.println(curReceiver.applicationInfo.sourceDir); } } - String stateStr = " (?)"; - switch (state) { - case IDLE: stateStr=" (IDLE)"; break; - case APP_RECEIVE: stateStr=" (APP_RECEIVE)"; break; - case CALL_IN_RECEIVE: stateStr=" (CALL_IN_RECEIVE)"; break; - case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break; + if (state != IDLE) { + String stateStr = " (?)"; + switch (state) { + case APP_RECEIVE: stateStr=" (APP_RECEIVE)"; break; + case CALL_IN_RECEIVE: stateStr=" (CALL_IN_RECEIVE)"; break; + case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break; + } + pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr); } - pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr); final int N = receivers != null ? receivers.size() : 0; String p2 = prefix + " "; PrintWriterPrinter printer = new PrintWriterPrinter(pw); @@ -168,7 +169,8 @@ class BroadcastRecord extends Binder { int _callingPid, int _callingUid, String _requiredPermission, List _receivers, IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras, boolean _serialized, - boolean _sticky, boolean _initialSticky, boolean _onlySendToCaller) { + boolean _sticky, boolean _initialSticky, + int _userId) { queue = _queue; intent = _intent; callerApp = _callerApp; @@ -184,7 +186,7 @@ class BroadcastRecord extends Binder { ordered = _serialized; sticky = _sticky; initialSticky = _initialSticky; - onlySendToCaller = _onlySendToCaller; + userId = _userId; nextReceiver = 0; state = IDLE; } diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index d3b8510..8e70376 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -54,11 +54,12 @@ class PendingIntentRecord extends IIntentSender.Stub { String[] allResolvedTypes; final int flags; final int hashCode; + final int userId; private static final int ODD_PRIME_NUMBER = 37; Key(int _t, String _p, ActivityRecord _a, String _w, - int _r, Intent[] _i, String[] _it, int _f, Bundle _o) { + int _r, Intent[] _i, String[] _it, int _f, Bundle _o, int _userId) { type = _t; packageName = _p; activity = _a; @@ -70,10 +71,12 @@ class PendingIntentRecord extends IIntentSender.Stub { allResolvedTypes = _it; flags = _f; options = _o; - + userId = _userId; + int hash = 23; hash = (ODD_PRIME_NUMBER*hash) + _f; hash = (ODD_PRIME_NUMBER*hash) + _r; + hash = (ODD_PRIME_NUMBER*hash) + _userId; if (_w != null) { hash = (ODD_PRIME_NUMBER*hash) + _w.hashCode(); } @@ -102,6 +105,9 @@ class PendingIntentRecord extends IIntentSender.Stub { if (type != other.type) { return false; } + if (userId != other.userId){ + return false; + } if (!packageName.equals(other.packageName)) { return false; } @@ -156,7 +162,7 @@ class PendingIntentRecord extends IIntentSender.Stub { + " intent=" + (requestIntent != null ? requestIntent.toShortString(false, true, false, false) : "<null>") - + " flags=0x" + Integer.toHexString(flags) + "}"; + + " flags=0x" + Integer.toHexString(flags) + " u=" + userId + "}"; } String typeName() { diff --git a/services/java/com/android/server/am/UserStartedState.java b/services/java/com/android/server/am/UserStartedState.java new file mode 100644 index 0000000..3f3ed85 --- /dev/null +++ b/services/java/com/android/server/am/UserStartedState.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import java.io.PrintWriter; +import java.util.ArrayList; + +import android.app.IStopUserCallback; +import android.os.UserHandle; + +public class UserStartedState { + public final static int STATE_BOOTING = 0; + public final static int STATE_RUNNING = 1; + public final static int STATE_STOPPING = 2; + + public final UserHandle mHandle; + public final ArrayList<IStopUserCallback> mStopCallbacks + = new ArrayList<IStopUserCallback>(); + + public int mState = STATE_BOOTING; + + public UserStartedState(UserHandle handle, boolean initial) { + mHandle = handle; + } + + void dump(String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("mState="); pw.println(mState); + } +} diff --git a/services/java/com/android/server/display/DisplayAdapter.java b/services/java/com/android/server/display/DisplayAdapter.java index f9fa7a8..d19fe01 100644 --- a/services/java/com/android/server/display/DisplayAdapter.java +++ b/services/java/com/android/server/display/DisplayAdapter.java @@ -16,33 +16,96 @@ package com.android.server.display; +import android.content.Context; +import android.os.Handler; + +import java.io.PrintWriter; + /** * A display adapter makes zero or more display devices available to the system * and provides facilities for discovering when displays are connected or disconnected. * <p> * For now, all display adapters are registered in the system server but * in principle it could be done from other processes. + * </p><p> + * Display devices are not thread-safe and must only be accessed + * on the display manager service's handler thread. * </p> */ -public abstract class DisplayAdapter { +public class DisplayAdapter { + private final Context mContext; + private final String mName; + private final Handler mHandler; + private Listener mListener; + + public static final int DISPLAY_DEVICE_EVENT_ADDED = 1; + public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2; + public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3; + + public DisplayAdapter(Context context, String name) { + mContext = context; + mName = name; + mHandler = new Handler(); + } + + public final Context getContext() { + return mContext; + } + + public final Handler getHandler() { + return mHandler; + } + /** * Gets the display adapter name for debugging purposes. * * @return The display adapter name. */ - public abstract String getName(); + public final String getName() { + return mName; + } /** * Registers the display adapter with the display manager. - * The display adapter should register any built-in display devices now. - * Other display devices can be registered dynamically later. * - * @param listener The listener for callbacks. + * @param listener The listener for callbacks. The listener will + * be invoked on the display manager service's handler thread. + */ + public final void register(Listener listener) { + mListener = listener; + onRegister(); + } + + /** + * Dumps the local state of the display adapter. + */ + public void dump(PrintWriter pw) { + } + + /** + * Called when the display adapter is registered. + * + * The display adapter should register any built-in display devices as soon as possible. + * The boot process will wait for the default display to be registered. + * + * Other display devices can be registered dynamically later. */ - public abstract void register(Listener listener); + protected void onRegister() { + } + + /** + * Sends a display device event to the display adapter listener asynchronously. + */ + protected void sendDisplayDeviceEvent(final DisplayDevice device, final int event) { + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onDisplayDeviceEvent(device, event); + } + }); + } public interface Listener { - public void onDisplayDeviceAdded(DisplayDevice device); - public void onDisplayDeviceRemoved(DisplayDevice device); + public void onDisplayDeviceEvent(DisplayDevice device, int event); } } diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java index 57002ff..c83ce96 100644 --- a/services/java/com/android/server/display/DisplayDevice.java +++ b/services/java/com/android/server/display/DisplayDevice.java @@ -16,17 +16,43 @@ package com.android.server.display; +import android.os.IBinder; + /** * Represents a physical display device such as the built-in display * an external monitor, or a WiFi display. + * <p> + * Display devices are not thread-safe and must only be accessed + * on the display manager service's handler thread. + * </p> */ public abstract class DisplayDevice { + private final DisplayAdapter mDisplayAdapter; + private final IBinder mDisplayToken; + + public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken) { + mDisplayAdapter = displayAdapter; + mDisplayToken = displayToken; + } + /** - * Gets the display adapter that makes the display device available. + * Gets the display adapter that owns the display device. * * @return The display adapter. */ - public abstract DisplayAdapter getAdapter(); + public final DisplayAdapter getAdapter() { + return mDisplayAdapter; + } + + /** + * Gets the Surface Flinger display token for this display. + * + * @return The display token, or null if the display is not being managed + * by Surface Flinger. + */ + public final IBinder getDisplayToken() { + return mDisplayToken; + } /** * Gets information about the display device. @@ -34,4 +60,12 @@ public abstract class DisplayDevice { * @param outInfo The object to populate with the information. */ public abstract void getInfo(DisplayDeviceInfo outInfo); + + // For debugging purposes. + @Override + public String toString() { + DisplayDeviceInfo info = new DisplayDeviceInfo(); + getInfo(info); + return info.toString() + ", owner=\"" + mDisplayAdapter.getName() + "\""; + } } diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java index 9c0f964..c7b8c24 100644 --- a/services/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/java/com/android/server/display/DisplayDeviceInfo.java @@ -20,6 +20,9 @@ package com.android.server.display; * Describes the characteristics of a physical display device. */ public final class DisplayDeviceInfo { + public static final int FLAG_DEFAULT_DISPLAY = 1 << 0; + public static final int FLAG_SECURE = 1 << 1; + /** * Gets the name of the display device, which may be derived from * EDID or other sources. The name may be displayed to the user. @@ -43,6 +46,8 @@ public final class DisplayDeviceInfo { public float xDpi; public float yDpi; + public int flags; + public void copyFrom(DisplayDeviceInfo other) { name = other.name; width = other.width; @@ -51,12 +56,25 @@ public final class DisplayDeviceInfo { densityDpi = other.densityDpi; xDpi = other.xDpi; yDpi = other.yDpi; + flags = other.flags; } // For debugging purposes @Override public String toString() { return "\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, " - + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"; + + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi" + + flagsToString(flags); + } + + private static String flagsToString(int flags) { + StringBuilder msg = new StringBuilder(); + if ((flags & FLAG_DEFAULT_DISPLAY) != 0) { + msg.append(", FLAG_DEFAULT_DISPLAY"); + } + if ((flags & FLAG_SECURE) != 0) { + msg.append(", FLAG_SECURE"); + } + return msg.toString(); } } diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java index 2ebad1d..cf835ef 100644 --- a/services/java/com/android/server/display/DisplayManagerService.java +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -16,57 +16,186 @@ package com.android.server.display; +import com.android.internal.util.IndentingPrintWriter; + import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; -import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.IDisplayManager; +import android.hardware.display.IDisplayManagerCallback; import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.SystemClock; import android.os.SystemProperties; +import android.util.Slog; +import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; /** - * Manages the properties, media routing and power state of attached displays. + * Manages attached displays. * <p> - * The display manager service does not own or directly control the displays. - * Instead, other components in the system register their display adapters with the - * display manager service which acts as a central controller. + * The {@link DisplayManagerService} manages the global lifecycle of displays, + * decides how to configure logical displays based on the physical display devices currently + * attached, sends notifications to the system and to applications when the state + * changes, and so on. + * </p><p> + * The display manager service relies on a collection of {@link DisplayAdapter} components, + * for discovering and configuring physical display devices attached to the system. + * There are separate display adapters for each manner that devices are attached: + * one display adapter for built-in local displays, one for simulated non-functional + * displays when the system is headless, one for simulated overlay displays used for + * development, one for wifi displays, etc. + * </p><p> + * Display adapters are only weakly coupled to the display manager service. + * Display adapters communicate changes in display device state to the display manager + * service asynchronously via a {@link DisplayAdapter.DisplayAdapterListener} registered + * by the display manager service. This separation of concerns is important for + * two main reasons. First, it neatly encapsulates the responsibilities of these + * two classes: display adapters handle individual display devices whereas + * the display manager service handles the global state. Second, it eliminates + * the potential for deadlocks resulting from asynchronous display device discovery. + * </p><p> + * To keep things simple, display adapters and display devices are single-threaded + * and are only accessed on the display manager's handler thread. Of course, the + * display manager must be accessible by multiple thread (especially for + * incoming binder calls) so all of the display manager's state is synchronized + * and guarded by a lock. * </p> */ public final class DisplayManagerService extends IDisplayManager.Stub { private static final String TAG = "DisplayManagerService"; + private static final boolean DEBUG = false; private static final String SYSTEM_HEADLESS = "ro.config.headless"; + private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000; + + private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER = 1; + private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2; + private static final int MSG_DELIVER_DISPLAY_EVENT = 3; private final Object mLock = new Object(); private final Context mContext; private final boolean mHeadless; + private final DisplayManagerHandler mHandler; + private final DisplayAdapterListener mDisplayAdapterListener = new DisplayAdapterListener(); + private final SparseArray<CallbackRecord> mCallbacks = + new SparseArray<CallbackRecord>(); + + // List of all currently registered display adapters. private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>(); - private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo(); - public DisplayManagerService(Context context) { + // List of all currently connected display devices. + private final ArrayList<DisplayDevice> mDisplayDevices = new ArrayList<DisplayDevice>(); + + // List of all logical displays, indexed by logical display id. + private final SparseArray<LogicalDisplay> mLogicalDisplays = new SparseArray<LogicalDisplay>(); + private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1; + + // True if in safe mode. + // This option may disable certain display adapters. + private boolean mSafeMode; + + // True if we are in a special boot mode where only core applications and + // services should be started. This option may disable certain display adapters. + private boolean mOnlyCore; + + // Temporary callback list, used when sending display events to applications. + private ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>(); + + public DisplayManagerService(Context context, Handler uiHandler) { mContext = context; mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1"); - registerDefaultDisplayAdapter(); + mHandler = new DisplayManagerHandler(uiHandler.getLooper()); + mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER); + } + + /** + * Pauses the boot process to wait for the first display to be initialized. + */ + public boolean waitForDefaultDisplay() { + synchronized (mLock) { + long timeout = SystemClock.uptimeMillis() + WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT; + while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null) { + long delay = timeout - SystemClock.uptimeMillis(); + if (delay <= 0) { + return false; + } + if (DEBUG) { + Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay); + } + try { + mLock.wait(delay); + } catch (InterruptedException ex) { + } + } + } + return true; + } + + /** + * Called when the system is ready to go. + */ + public void systemReady(boolean safeMode, boolean onlyCore) { + synchronized (mLock) { + mSafeMode = safeMode; + mOnlyCore = onlyCore; + } + mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS); } + // Runs on handler. private void registerDefaultDisplayAdapter() { + // Register default display adapter. if (mHeadless) { registerDisplayAdapter(new HeadlessDisplayAdapter(mContext)); } else { - registerDisplayAdapter(new SurfaceFlingerDisplayAdapter(mContext)); + registerDisplayAdapter(new LocalDisplayAdapter(mContext)); + } + } + + // Runs on handler. + private void registerAdditionalDisplayAdapters() { + if (shouldRegisterNonEssentialDisplayAdapters()) { + registerDisplayAdapter(new OverlayDisplayAdapter(mContext)); + } + } + + private boolean shouldRegisterNonEssentialDisplayAdapters() { + // In safe mode, we disable non-essential display adapters to give the user + // an opportunity to fix broken settings or other problems that might affect + // system stability. + // In only-core mode, we disable non-essential display adapters to minimize + // the number of dependencies that are started while in this mode and to + // prevent problems that might occur due to the device being encrypted. + synchronized (mLock) { + return !mSafeMode && !mOnlyCore; } } + // Runs on handler. + private void registerDisplayAdapter(DisplayAdapter adapter) { + synchronized (mLock) { + mDisplayAdapters.add(adapter); + } + + adapter.register(mDisplayAdapterListener); + } + // FIXME: this isn't the right API for the long term public void getDefaultExternalDisplayDeviceInfo(DisplayDeviceInfo info) { // hardcoded assuming 720p touch screen plugged into HDMI and USB @@ -85,71 +214,224 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } /** - * Save away new DisplayInfo data. - * @param displayId The local DisplayInfo to store the new data in. + * Sets the new logical display orientation. + * + * @param displayId The logical display id. + * @param orientation One of the Surface.ROTATION_* constants. + */ + public void setDisplayOrientation(int displayId, int orientation) { + synchronized (mLock) { + // TODO: update mirror transforms + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null && display.mPrimaryDisplayDevice != null) { + IBinder displayToken = display.mPrimaryDisplayDevice.getDisplayToken(); + if (displayToken != null) { + Surface.openTransaction(); + try { + Surface.setDisplayOrientation(displayToken, orientation); + } finally { + Surface.closeTransaction(); + } + } + + display.mBaseDisplayInfo.rotation = orientation; + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); + } + } + } + + /** + * Overrides the display information of a particular logical display. + * This is used by the window manager to control the size and characteristics + * of the default display. + * + * @param displayId The logical display id. * @param info The new data to be stored. */ - public void setDisplayInfo(int displayId, DisplayInfo info) { + public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) { synchronized (mLock) { - if (displayId != Display.DEFAULT_DISPLAY) { - throw new UnsupportedOperationException(); + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null) { + if (info != null) { + if (display.mOverrideDisplayInfo == null) { + display.mOverrideDisplayInfo = new DisplayInfo(); + } + display.mOverrideDisplayInfo.copyFrom(info); + } else { + display.mOverrideDisplayInfo = null; + } + + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); } - mDefaultDisplayInfo.copyFrom(info); } } /** - * Return requested DisplayInfo. - * @param displayId The data to retrieve. - * @param outInfo The structure to receive the data. + * Returns information about the specified logical display. + * + * @param displayId The logical display id. + * @param The logical display info, or null if the display does not exist. */ @Override // Binder call - public boolean getDisplayInfo(int displayId, DisplayInfo outInfo) { + public DisplayInfo getDisplayInfo(int displayId) { synchronized (mLock) { - if (displayId != Display.DEFAULT_DISPLAY) { - return false; + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null) { + if (display.mOverrideDisplayInfo != null) { + return new DisplayInfo(display.mOverrideDisplayInfo); + } + return new DisplayInfo(display.mBaseDisplayInfo); } - outInfo.copyFrom(mDefaultDisplayInfo); - return true; + return null; } } - private void registerDisplayAdapter(DisplayAdapter adapter) { - mDisplayAdapters.add(adapter); - adapter.register(new DisplayAdapter.Listener() { - @Override - public void onDisplayDeviceAdded(DisplayDevice device) { - DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); - device.getInfo(deviceInfo); - copyDisplayInfoFromDeviceInfo(mDefaultDisplayInfo, deviceInfo); + @Override // Binder call + public int[] getDisplayIds() { + synchronized (mLock) { + final int count = mLogicalDisplays.size(); + int[] displayIds = new int[count]; + for (int i = 0; i > count; i++) { + displayIds[i] = mLogicalDisplays.keyAt(i); } + return displayIds; + } + } - @Override - public void onDisplayDeviceRemoved(DisplayDevice device) { + @Override // Binder call + public void registerCallback(IDisplayManagerCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mLock) { + int callingPid = Binder.getCallingPid(); + if (mCallbacks.get(callingPid) != null) { + throw new SecurityException("The calling process has already " + + "registered an IDisplayManagerCallback."); } - }); + + CallbackRecord record = new CallbackRecord(callingPid, callback); + try { + IBinder binder = callback.asBinder(); + binder.linkToDeath(record, 0); + } catch (RemoteException ex) { + // give up + throw new RuntimeException(ex); + } + + mCallbacks.put(callingPid, record); + } + } + + private void onCallbackDied(int pid) { + synchronized (mLock) { + mCallbacks.remove(pid); + } + } + + // Runs on handler. + private void handleDisplayDeviceAdded(DisplayDevice device) { + synchronized (mLock) { + if (mDisplayDevices.contains(device)) { + Slog.w(TAG, "Attempted to add already added display device: " + device); + return; + } + + mDisplayDevices.add(device); + + LogicalDisplay display = new LogicalDisplay(device); + display.updateFromPrimaryDisplayDevice(); + + boolean isDefault = (display.mPrimaryDisplayDeviceInfo.flags + & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0; + if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) { + Slog.w(TAG, "Attempted to add a second default device: " + device); + isDefault = false; + } + + int displayId = isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++; + mLogicalDisplays.put(displayId, display); + + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED); + + // Wake up waitForDefaultDisplay. + if (isDefault) { + mLock.notifyAll(); + } + } + } + + // Runs on handler. + private void handleDisplayDeviceChanged(DisplayDevice device) { + synchronized (mLock) { + if (!mDisplayDevices.contains(device)) { + Slog.w(TAG, "Attempted to change non-existent display device: " + device); + return; + } + + for (int i = mLogicalDisplays.size(); i-- > 0; ) { + LogicalDisplay display = mLogicalDisplays.valueAt(i); + if (display.mPrimaryDisplayDevice == device) { + final int displayId = mLogicalDisplays.keyAt(i); + display.updateFromPrimaryDisplayDevice(); + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); + } + } + } + } + + // Runs on handler. + private void handleDisplayDeviceRemoved(DisplayDevice device) { + synchronized (mLock) { + if (!mDisplayDevices.remove(device)) { + Slog.w(TAG, "Attempted to remove non-existent display device: " + device); + return; + } + + for (int i = mLogicalDisplays.size(); i-- > 0; ) { + LogicalDisplay display = mLogicalDisplays.valueAt(i); + if (display.mPrimaryDisplayDevice == device) { + final int displayId = mLogicalDisplays.keyAt(i); + mLogicalDisplays.removeAt(i); + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED); + } + } + } + } + + // Posts a message to send a display event at the next opportunity. + private void sendDisplayEventLocked(int displayId, int event) { + Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event); + mHandler.sendMessage(msg); } - private void copyDisplayInfoFromDeviceInfo( - DisplayInfo displayInfo, DisplayDeviceInfo deviceInfo) { - // Bootstrap the logical display using the physical display. - displayInfo.appWidth = deviceInfo.width; - displayInfo.appHeight = deviceInfo.height; - displayInfo.logicalWidth = deviceInfo.width; - displayInfo.logicalHeight = deviceInfo.height; - displayInfo.rotation = Surface.ROTATION_0; - displayInfo.refreshRate = deviceInfo.refreshRate; - displayInfo.logicalDensityDpi = deviceInfo.densityDpi; - displayInfo.physicalXDpi = deviceInfo.xDpi; - displayInfo.physicalYDpi = deviceInfo.yDpi; - displayInfo.smallestNominalAppWidth = deviceInfo.width; - displayInfo.smallestNominalAppHeight = deviceInfo.height; - displayInfo.largestNominalAppWidth = deviceInfo.width; - displayInfo.largestNominalAppHeight = deviceInfo.height; + // Runs on handler. + // This method actually sends display event notifications. + // Note that it must be very careful not to be holding the lock while sending + // is in progress. + private void deliverDisplayEvent(int displayId, int event) { + if (DEBUG) { + Slog.d(TAG, "Delivering display event: displayId=" + displayId + ", event=" + event); + } + + final int count; + synchronized (mLock) { + count = mCallbacks.size(); + mTempCallbacks.clear(); + for (int i = 0; i < count; i++) { + mTempCallbacks.add(mCallbacks.valueAt(i)); + } + } + + for (int i = 0; i < count; i++) { + mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event); + } + mTempCallbacks.clear(); } @Override // Binder call - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (mContext == null || mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -158,19 +440,159 @@ public final class DisplayManagerService extends IDisplayManager.Stub { return; } - pw.println("DISPLAY MANAGER (dumpsys display)\n"); + pw.println("DISPLAY MANAGER (dumpsys display)"); + pw.println(" mHeadless=" + mHeadless); - pw.println("Headless: " + mHeadless); + mHandler.runWithScissors(new Runnable() { + @Override + public void run() { + dumpLocal(pw); + } + }); + } + // Runs on handler. + private void dumpLocal(PrintWriter pw) { synchronized (mLock) { + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + + pw.println(); + pw.println("Display Adapters: size=" + mDisplayAdapters.size()); for (DisplayAdapter adapter : mDisplayAdapters) { - pw.println("Adapter: " + adapter.getName()); + pw.println(" " + adapter.getName()); + adapter.dump(ipw); + } + + pw.println(); + pw.println("Display Devices: size=" + mDisplayDevices.size()); + for (DisplayDevice device : mDisplayDevices) { + pw.println(" " + device); + } + + final int logicalDisplayCount = mLogicalDisplays.size(); + pw.println(); + pw.println("Logical Displays: size=" + logicalDisplayCount); + for (int i = 0; i < logicalDisplayCount; i++) { + int displayId = mLogicalDisplays.keyAt(i); + LogicalDisplay display = mLogicalDisplays.valueAt(i); + pw.println(" Display " + displayId + ":"); + pw.println(" mPrimaryDisplayDevice=" + display.mPrimaryDisplayDevice); + pw.println(" mBaseDisplayInfo=" + display.mBaseDisplayInfo); + pw.println(" mOverrideDisplayInfo=" + + display.mOverrideDisplayInfo); + } + } + } + + private final class DisplayManagerHandler extends Handler { + public DisplayManagerHandler(Looper looper) { + super(looper, null, true /*async*/); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER: + registerDefaultDisplayAdapter(); + break; + + case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS: + registerAdditionalDisplayAdapters(); + break; + + case MSG_DELIVER_DISPLAY_EVENT: + deliverDisplayEvent(msg.arg1, msg.arg2); + break; + } + } + } + + private final class DisplayAdapterListener implements DisplayAdapter.Listener { + @Override + public void onDisplayDeviceEvent(DisplayDevice device, int event) { + switch (event) { + case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED: + handleDisplayDeviceAdded(device); + break; + + case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED: + handleDisplayDeviceChanged(device); + break; + + case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED: + handleDisplayDeviceRemoved(device); + break; + } + } + } + + private final class CallbackRecord implements DeathRecipient { + private final int mPid; + private final IDisplayManagerCallback mCallback; + + public CallbackRecord(int pid, IDisplayManagerCallback callback) { + mPid = pid; + mCallback = callback; + } + + @Override + public void binderDied() { + if (DEBUG) { + Slog.d(TAG, "Display listener for pid " + mPid + " died."); + } + onCallbackDied(mPid); + } + + public void notifyDisplayEventAsync(int displayId, int event) { + try { + mCallback.onDisplayEvent(displayId, event); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + + mPid + " that displays changed, assuming it died.", ex); + binderDied(); } + } + } - pw.println("Default display info: " + mDefaultDisplayInfo); + /** + * Each logical display is primarily associated with one display device. + * The primary display device is nominally responsible for the basic properties + * of the logical display such as its size, refresh rate, and dpi. + * + * A logical display may be mirrored onto other display devices besides its + * primary display device, but it always remains bound to its primary. + * Note that the contents of a logical display may not always be visible, even + * on its primary display device, such as in the case where the logical display's + * primary display device is currently mirroring content from a different logical display. + */ + private final static class LogicalDisplay { + public final DisplayInfo mBaseDisplayInfo = new DisplayInfo(); + public DisplayInfo mOverrideDisplayInfo; // set by the window manager + + public final DisplayDevice mPrimaryDisplayDevice; + public final DisplayDeviceInfo mPrimaryDisplayDeviceInfo = new DisplayDeviceInfo(); + + public LogicalDisplay(DisplayDevice primaryDisplayDevice) { + mPrimaryDisplayDevice = primaryDisplayDevice; } - pw.println("Default display: " - + DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY)); + public void updateFromPrimaryDisplayDevice() { + // Bootstrap the logical display using its associated primary physical display. + mPrimaryDisplayDevice.getInfo(mPrimaryDisplayDeviceInfo); + + mBaseDisplayInfo.appWidth = mPrimaryDisplayDeviceInfo.width; + mBaseDisplayInfo.appHeight = mPrimaryDisplayDeviceInfo.height; + mBaseDisplayInfo.logicalWidth = mPrimaryDisplayDeviceInfo.width; + mBaseDisplayInfo.logicalHeight = mPrimaryDisplayDeviceInfo.height; + mBaseDisplayInfo.rotation = Surface.ROTATION_0; + mBaseDisplayInfo.refreshRate = mPrimaryDisplayDeviceInfo.refreshRate; + mBaseDisplayInfo.logicalDensityDpi = mPrimaryDisplayDeviceInfo.densityDpi; + mBaseDisplayInfo.physicalXDpi = mPrimaryDisplayDeviceInfo.xDpi; + mBaseDisplayInfo.physicalYDpi = mPrimaryDisplayDeviceInfo.yDpi; + mBaseDisplayInfo.smallestNominalAppWidth = mPrimaryDisplayDeviceInfo.width; + mBaseDisplayInfo.smallestNominalAppHeight = mPrimaryDisplayDeviceInfo.height; + mBaseDisplayInfo.largestNominalAppWidth = mPrimaryDisplayDeviceInfo.width; + mBaseDisplayInfo.largestNominalAppHeight = mPrimaryDisplayDeviceInfo.height; + } } } diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java index 17c2360..f5c78b9 100644 --- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java +++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java @@ -21,42 +21,40 @@ import android.util.DisplayMetrics; /** * Provides a fake default display for headless systems. + * <p> + * Display adapters are not thread-safe and must only be accessed + * on the display manager service's handler thread. + * </p> */ public final class HeadlessDisplayAdapter extends DisplayAdapter { - private final Context mContext; - private final HeadlessDisplayDevice mDefaultDisplayDevice; + private static final String TAG = "HeadlessDisplayAdapter"; public HeadlessDisplayAdapter(Context context) { - mContext = context; - mDefaultDisplayDevice = new HeadlessDisplayDevice(); + super(context, TAG); } @Override - public String getName() { - return "HeadlessDisplayAdapter"; - } - - @Override - public void register(Listener listener) { - listener.onDisplayDeviceAdded(mDefaultDisplayDevice); + protected void onRegister() { + sendDisplayDeviceEvent(new HeadlessDisplayDevice(), DISPLAY_DEVICE_EVENT_ADDED); } private final class HeadlessDisplayDevice extends DisplayDevice { - @Override - public DisplayAdapter getAdapter() { - return HeadlessDisplayAdapter.this; + public HeadlessDisplayDevice() { + super(HeadlessDisplayAdapter.this, null); } @Override public void getInfo(DisplayDeviceInfo outInfo) { - outInfo.name = mContext.getResources().getString( - com.android.internal.R.string.display_manager_built_in_display); + outInfo.name = getContext().getResources().getString( + com.android.internal.R.string.display_manager_built_in_display_name); outInfo.width = 640; outInfo.height = 480; outInfo.refreshRate = 60; outInfo.densityDpi = DisplayMetrics.DENSITY_DEFAULT; outInfo.xDpi = 160; outInfo.yDpi = 160; + outInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY + | DisplayDeviceInfo.FLAG_SECURE; } } } diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java new file mode 100644 index 0000000..73544fc --- /dev/null +++ b/services/java/com/android/server/display/LocalDisplayAdapter.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.content.Context; +import android.os.IBinder; +import android.view.Surface; +import android.view.Surface.PhysicalDisplayInfo; + +/** + * A display adapter for the local displays managed by Surface Flinger. + * <p> + * Display adapters are not thread-safe and must only be accessed + * on the display manager service's handler thread. + * </p> + */ +public final class LocalDisplayAdapter extends DisplayAdapter { + private static final String TAG = "LocalDisplayAdapter"; + + public LocalDisplayAdapter(Context context) { + super(context, TAG); + } + + @Override + protected void onRegister() { + // TODO: listen for notifications from Surface Flinger about + // built-in displays being added or removed and rescan as needed. + IBinder displayToken = Surface.getBuiltInDisplay(Surface.BUILT_IN_DISPLAY_ID_MAIN); + sendDisplayDeviceEvent(new LocalDisplayDevice(displayToken, true), + DISPLAY_DEVICE_EVENT_ADDED); + } + + private final class LocalDisplayDevice extends DisplayDevice { + private final boolean mIsDefault; + + public LocalDisplayDevice(IBinder displayToken, boolean isDefault) { + super(LocalDisplayAdapter.this, displayToken); + mIsDefault = isDefault; + } + + @Override + public void getInfo(DisplayDeviceInfo outInfo) { + PhysicalDisplayInfo phys = new PhysicalDisplayInfo(); + Surface.getDisplayInfo(getDisplayToken(), phys); + + outInfo.name = getContext().getResources().getString( + com.android.internal.R.string.display_manager_built_in_display_name); + outInfo.width = phys.width; + outInfo.height = phys.height; + outInfo.refreshRate = phys.refreshRate; + outInfo.densityDpi = (int)(phys.density * 160 + 0.5f); + outInfo.xDpi = phys.xDpi; + outInfo.yDpi = phys.yDpi; + if (mIsDefault) { + outInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY + | DisplayDeviceInfo.FLAG_SECURE; + } + } + } +} diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java new file mode 100644 index 0000000..476d21a --- /dev/null +++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.content.Context; +import android.database.ContentObserver; +import android.graphics.SurfaceTexture; +import android.hardware.display.DisplayManager; +import android.os.IBinder; +import android.provider.Settings; +import android.util.DisplayMetrics; +import android.util.Slog; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.Surface; +import android.view.TextureView; +import android.view.TextureView.SurfaceTextureListener; +import android.view.View; +import android.view.WindowManager; +import android.widget.TextView; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A display adapter that uses overlay windows to simulate secondary displays + * for development purposes. Use Development Settings to enable one or more + * overlay displays. + * <p> + * Display adapters are not thread-safe and must only be accessed + * on the display manager service's handler thread. + * </p> + */ +public final class OverlayDisplayAdapter extends DisplayAdapter { + private static final String TAG = "OverlayDisplayAdapter"; + + private static final int MIN_WIDTH = 100; + private static final int MIN_HEIGHT = 100; + private static final int MAX_WIDTH = 4096; + private static final int MAX_HEIGHT = 4096; + + private static final Pattern SETTING_PATTERN = + Pattern.compile("(\\d+)x(\\d+)/(\\d+)"); + + private final ArrayList<Overlay> mOverlays = new ArrayList<Overlay>(); + private String mCurrentOverlaySetting = ""; + + public OverlayDisplayAdapter(Context context) { + super(context, TAG); + } + + @Override + public void dump(PrintWriter pw) { + pw.println("mCurrentOverlaySetting=" + mCurrentOverlaySetting); + pw.println("mOverlays: size=" + mOverlays.size()); + for (Overlay overlay : mOverlays) { + overlay.dump(pw); + } + } + + @Override + protected void onRegister() { + getContext().getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.Secure.OVERLAY_DISPLAY_DEVICES), true, + new ContentObserver(getHandler()) { + @Override + public void onChange(boolean selfChange) { + updateOverlayDisplayDevices(); + } + }); + updateOverlayDisplayDevices(); + } + + private void updateOverlayDisplayDevices() { + String value = Settings.System.getString(getContext().getContentResolver(), + Settings.Secure.OVERLAY_DISPLAY_DEVICES); + if (value == null) { + value = ""; + } + + if (value.equals(mCurrentOverlaySetting)) { + return; + } + mCurrentOverlaySetting = value; + + if (!mOverlays.isEmpty()) { + Slog.i(TAG, "Dismissing all overlay display devices."); + for (Overlay overlay : mOverlays) { + overlay.dismiss(); + } + mOverlays.clear(); + } + + int number = 1; + for (String part : value.split(";")) { + if (number > 4) { + Slog.w(TAG, "Too many overlay display devices."); + } + Matcher matcher = SETTING_PATTERN.matcher(part); + if (matcher.matches()) { + try { + int width = Integer.parseInt(matcher.group(1), 10); + int height = Integer.parseInt(matcher.group(2), 10); + int densityDpi = Integer.parseInt(matcher.group(3), 10); + if (width >= MIN_WIDTH && width <= MAX_WIDTH + && height >= MIN_HEIGHT && height <= MAX_HEIGHT + && densityDpi >= DisplayMetrics.DENSITY_LOW + && densityDpi <= DisplayMetrics.DENSITY_XXHIGH) { + Slog.i(TAG, "Showing overlay display device #" + number + + ": width=" + width + ", height=" + height + + ", densityDpi=" + densityDpi); + mOverlays.add(new Overlay(number++, width, height, densityDpi)); + continue; + } + } catch (NumberFormatException ex) { + } + } else if (part.isEmpty()) { + continue; + } + Slog.w(TAG, "Malformed overlay display devices setting: \"" + value + "\""); + } + + for (Overlay overlay : mOverlays) { + overlay.show(); + } + } + + // Manages an overlay window. + private final class Overlay { + private final float INITIAL_SCALE = 0.5f; + private final float MIN_SCALE = 0.3f; + private final float MAX_SCALE = 1.0f; + private final float WINDOW_ALPHA = 0.8f; + + // When true, disables support for moving and resizing the overlay. + // The window is made non-touchable, which makes it possible to + // directly interact with the content underneath. + private final boolean DISABLE_MOVE_AND_RESIZE = false; + + private final DisplayManager mDisplayManager; + private final WindowManager mWindowManager; + + private final int mNumber; + private final int mWidth; + private final int mHeight; + private final int mDensityDpi; + + private final String mName; + private final String mTitle; + + private final Display mDefaultDisplay; + private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo(); + private final IBinder mDisplayToken; + private final OverlayDisplayDevice mDisplayDevice; + + private View mWindowContent; + private WindowManager.LayoutParams mWindowParams; + private TextureView mTextureView; + private TextView mTitleTextView; + private ScaleGestureDetector mScaleGestureDetector; + + private boolean mWindowVisible; + private int mWindowX; + private int mWindowY; + private float mWindowScale; + + private int mLiveTranslationX; + private int mLiveTranslationY; + private float mLiveScale = 1.0f; + + private int mDragPointerId; + private float mDragTouchX; + private float mDragTouchY; + + public Overlay(int number, int width, int height, int densityDpi) { + Context context = getContext(); + mDisplayManager = (DisplayManager)context.getSystemService( + Context.DISPLAY_SERVICE); + mWindowManager = (WindowManager)context.getSystemService( + Context.WINDOW_SERVICE); + + mNumber = number; + mWidth = width; + mHeight = height; + mDensityDpi = densityDpi; + + mName = context.getResources().getString( + com.android.internal.R.string.display_manager_overlay_display_name, number); + mTitle = context.getResources().getString( + com.android.internal.R.string.display_manager_overlay_display_title, + mNumber, mWidth, mHeight, mDensityDpi); + + mDefaultDisplay = mWindowManager.getDefaultDisplay(); + updateDefaultDisplayInfo(); + + mDisplayToken = Surface.createDisplay(mName); + mDisplayDevice = new OverlayDisplayDevice(mDisplayToken, mName, + mDefaultDisplayInfo.refreshRate, mDensityDpi); + + createWindow(); + } + + public void show() { + if (!mWindowVisible) { + mDisplayManager.registerDisplayListener(mDisplayListener, null); + if (!updateDefaultDisplayInfo()) { + mDisplayManager.unregisterDisplayListener(mDisplayListener); + return; + } + + clearLiveState(); + updateWindowParams(); + mWindowManager.addView(mWindowContent, mWindowParams); + mWindowVisible = true; + } + } + + public void dismiss() { + if (mWindowVisible) { + mDisplayManager.unregisterDisplayListener(mDisplayListener); + mWindowManager.removeView(mWindowContent); + mWindowVisible = false; + } + } + + public void relayout() { + if (mWindowVisible) { + updateWindowParams(); + mWindowManager.updateViewLayout(mWindowContent, mWindowParams); + } + } + + public void dump(PrintWriter pw) { + pw.println(" #" + mNumber + ": " + + mWidth + "x" + mHeight + ", " + mDensityDpi + " dpi"); + pw.println(" mName=" + mName); + pw.println(" mWindowVisible=" + mWindowVisible); + pw.println(" mWindowX=" + mWindowX); + pw.println(" mWindowY=" + mWindowY); + pw.println(" mWindowScale=" + mWindowScale); + pw.println(" mWindowParams=" + mWindowParams); + pw.println(" mLiveTranslationX=" + mLiveTranslationX); + pw.println(" mLiveTranslationY=" + mLiveTranslationY); + pw.println(" mLiveScale=" + mLiveScale); + } + + private boolean updateDefaultDisplayInfo() { + if (!mDefaultDisplay.getDisplayInfo(mDefaultDisplayInfo)) { + Slog.w(TAG, "Cannot show overlay display because there is no " + + "default display upon which to show it."); + return false; + } + return true; + } + + private void createWindow() { + Context context = getContext(); + LayoutInflater inflater = LayoutInflater.from(context); + + mWindowContent = inflater.inflate( + com.android.internal.R.layout.overlay_display_window, null); + mWindowContent.setOnTouchListener(mOnTouchListener); + + mTextureView = (TextureView)mWindowContent.findViewById( + com.android.internal.R.id.overlay_display_window_texture); + mTextureView.setPivotX(0); + mTextureView.setPivotY(0); + mTextureView.getLayoutParams().width = mWidth; + mTextureView.getLayoutParams().height = mHeight; + mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); + + mTitleTextView = (TextView)mWindowContent.findViewById( + com.android.internal.R.id.overlay_display_window_title); + mTitleTextView.setText(mTitle); + + mWindowParams = new WindowManager.LayoutParams( + WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY); + mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; + if (DISABLE_MOVE_AND_RESIZE) { + mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + } + mWindowParams.privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED; + mWindowParams.alpha = WINDOW_ALPHA; + mWindowParams.gravity = Gravity.TOP | Gravity.LEFT; + mWindowParams.setTitle(mTitle); + + mScaleGestureDetector = new ScaleGestureDetector(context, mOnScaleGestureListener); + + // By default, arrange the displays in the four corners. + mWindowVisible = false; + mWindowScale = INITIAL_SCALE; + if (mNumber == 2 || mNumber == 3) { + mWindowX = mDefaultDisplayInfo.logicalWidth; + } else { + mWindowX = 0; + } + if (mNumber == 2 || mNumber == 4) { + mWindowY = mDefaultDisplayInfo.logicalHeight; + } else { + mWindowY = 0; + } + } + + private void updateWindowParams() { + float scale = mWindowScale * mLiveScale; + if (mWidth * scale > mDefaultDisplayInfo.logicalWidth) { + scale = mDefaultDisplayInfo.logicalWidth / mWidth; + } + if (mHeight * scale > mDefaultDisplayInfo.logicalHeight) { + scale = mDefaultDisplayInfo.logicalHeight / mHeight; + } + scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale)); + + float offsetScale = (scale / mWindowScale - 1.0f) * 0.5f; + int width = (int)(mWidth * scale); + int height = (int)(mHeight * scale); + int x = mWindowX + mLiveTranslationX - (int)(width * offsetScale); + int y = mWindowY + mLiveTranslationY - (int)(height * offsetScale); + x = Math.max(0, Math.min(x, mDefaultDisplayInfo.logicalWidth - width)); + y = Math.max(0, Math.min(y, mDefaultDisplayInfo.logicalHeight - height)); + + mTextureView.setScaleX(scale); + mTextureView.setScaleY(scale); + + mWindowParams.x = x; + mWindowParams.y = y; + mWindowParams.width = width; + mWindowParams.height = height; + } + + private void saveWindowParams() { + mWindowX = mWindowParams.x; + mWindowY = mWindowParams.y; + mWindowScale = mTextureView.getScaleX(); + clearLiveState(); + } + + private void clearLiveState() { + mLiveTranslationX = 0; + mLiveTranslationY = 0; + mLiveScale = 1.0f; + } + + private final DisplayManager.DisplayListener mDisplayListener = + new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + } + + @Override + public void onDisplayChanged(int displayId) { + if (displayId == mDefaultDisplay.getDisplayId()) { + if (updateDefaultDisplayInfo()) { + relayout(); + } else { + dismiss(); + } + } + } + + @Override + public void onDisplayRemoved(int displayId) { + if (displayId == mDefaultDisplay.getDisplayId()) { + dismiss(); + } + } + }; + + private final SurfaceTextureListener mSurfaceTextureListener = + new SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + Surface.openTransaction(); + try { + Surface.setDisplaySurface(mDisplayToken, surface); + } finally { + Surface.closeTransaction(); + } + + mDisplayDevice.setSize(width, height); + sendDisplayDeviceEvent(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED); + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + sendDisplayDeviceEvent(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED); + + Surface.openTransaction(); + try { + Surface.setDisplaySurface(mDisplayToken, null); + } finally { + Surface.closeTransaction(); + } + return true; + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + mDisplayDevice.setSize(width, height); + sendDisplayDeviceEvent(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED); + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + } + }; + + private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent event) { + // Work in screen coordinates. + final float oldX = event.getX(); + final float oldY = event.getY(); + event.setLocation(event.getRawX(), event.getRawY()); + + mScaleGestureDetector.onTouchEvent(event); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + resetDrag(event); + break; + + case MotionEvent.ACTION_MOVE: + if (event.getPointerCount() == 1) { + int index = event.findPointerIndex(mDragPointerId); + if (index < 0) { + resetDrag(event); + } else { + mLiveTranslationX = (int)(event.getX(index) - mDragTouchX); + mLiveTranslationY = (int)(event.getY(index) - mDragTouchY); + relayout(); + } + } + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + saveWindowParams(); + break; + } + + // Revert to window coordinates. + event.setLocation(oldX, oldY); + return true; + } + + private void resetDrag(MotionEvent event) { + saveWindowParams(); + mDragPointerId = event.getPointerId(0); + mDragTouchX = event.getX(); + mDragTouchY = event.getY(); + } + }; + + private final ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener = + new ScaleGestureDetector.SimpleOnScaleGestureListener() { + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + saveWindowParams(); + mDragPointerId = -1; // cause drag to be reset + return true; + } + + @Override + public boolean onScale(ScaleGestureDetector detector) { + mLiveScale = detector.getScaleFactor(); + relayout(); + return false; + } + }; + } + + private final class OverlayDisplayDevice extends DisplayDevice { + private final String mName; + private final float mRefreshRate; + private final int mDensityDpi; + private int mWidth; + private int mHeight; + + public OverlayDisplayDevice(IBinder displayToken, String name, + float refreshRate, int densityDpi) { + super(OverlayDisplayAdapter.this, displayToken); + mName = name; + mRefreshRate = refreshRate; + mDensityDpi = densityDpi; + } + + public void setSize(int width, int height) { + mWidth = width; + mHeight = height; + } + + @Override + public void getInfo(DisplayDeviceInfo outInfo) { + outInfo.name = mName; + outInfo.width = mWidth; + outInfo.height = mHeight; + outInfo.refreshRate = mRefreshRate; + outInfo.densityDpi = mDensityDpi; + outInfo.xDpi = mDensityDpi; + outInfo.yDpi = mDensityDpi; + outInfo.flags = DisplayDeviceInfo.FLAG_SECURE; + } + } +} diff --git a/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java b/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java deleted file mode 100644 index 9531acb..0000000 --- a/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.display; - -import android.content.Context; - -/** - * A display adapter for the displays managed by Surface Flinger. - */ -public final class SurfaceFlingerDisplayAdapter extends DisplayAdapter { - private final Context mContext; - private final SurfaceFlingerDisplayDevice mDefaultDisplayDevice; - - private static native void nativeGetDefaultDisplayDeviceInfo(DisplayDeviceInfo outInfo); - - public SurfaceFlingerDisplayAdapter(Context context) { - mContext = context; - mDefaultDisplayDevice = new SurfaceFlingerDisplayDevice(); - } - - @Override - public String getName() { - return "SurfaceFlingerDisplayAdapter"; - } - - @Override - public void register(Listener listener) { - listener.onDisplayDeviceAdded(mDefaultDisplayDevice); - } - - private final class SurfaceFlingerDisplayDevice extends DisplayDevice { - @Override - public DisplayAdapter getAdapter() { - return SurfaceFlingerDisplayAdapter.this; - } - - @Override - public void getInfo(DisplayDeviceInfo outInfo) { - outInfo.name = mContext.getResources().getString( - com.android.internal.R.string.display_manager_built_in_display); - nativeGetDefaultDisplayDeviceInfo(outInfo); - } - } -} diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java index 541650e..dabcf2f 100644 --- a/services/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/java/com/android/server/net/LockdownVpnTracker.java @@ -55,6 +55,7 @@ public class LockdownVpnTracker { private static final int MAX_ERROR_COUNT = 4; private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET"; + private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS"; private final Context mContext; private final INetworkManagementService mNetService; @@ -84,9 +85,9 @@ public class LockdownVpnTracker { mVpn = Preconditions.checkNotNull(vpn); mProfile = Preconditions.checkNotNull(profile); - final Intent intent = new Intent(ACTION_LOCKDOWN_RESET); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mResetIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); + final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET); + resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0); } private BroadcastReceiver mResetReceiver = new BroadcastReceiver() { @@ -115,7 +116,7 @@ public class LockdownVpnTracker { final boolean egressChanged = egressProp == null || !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName()); if (egressDisconnected || egressChanged) { - clearSourceRules(); + clearSourceRulesLocked(); mAcceptedEgressIface = null; mVpn.stopLegacyVpn(); } @@ -150,7 +151,7 @@ public class LockdownVpnTracker { showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected); try { - clearSourceRules(); + clearSourceRulesLocked(); mNetService.setFirewallInterfaceRule(iface, true); mNetService.setFirewallEgressSourceRule(sourceAddr, true); @@ -167,7 +168,13 @@ public class LockdownVpnTracker { } public void init() { - Slog.d(TAG, "init()"); + synchronized (mStateLock) { + initLocked(); + } + } + + private void initLocked() { + Slog.d(TAG, "initLocked()"); mVpn.setEnableNotifications(false); @@ -188,7 +195,13 @@ public class LockdownVpnTracker { } public void shutdown() { - Slog.d(TAG, "shutdown()"); + synchronized (mStateLock) { + shutdownLocked(); + } + } + + private void shutdownLocked() { + Slog.d(TAG, "shutdownLocked()"); mAcceptedEgressIface = null; mErrorCount = 0; @@ -200,7 +213,7 @@ public class LockdownVpnTracker { } catch (RemoteException e) { throw new RuntimeException("Problem setting firewall rules", e); } - clearSourceRules(); + clearSourceRulesLocked(); hideNotification(); mContext.unregisterReceiver(mResetReceiver); @@ -208,15 +221,15 @@ public class LockdownVpnTracker { } public void reset() { - // cycle tracker, reset error count, and trigger retry - shutdown(); - init(); synchronized (mStateLock) { + // cycle tracker, reset error count, and trigger retry + shutdownLocked(); + initLocked(); handleStateChangedLocked(); } } - private void clearSourceRules() { + private void clearSourceRulesLocked() { try { if (mAcceptedIface != null) { mNetService.setFirewallInterfaceRule(mAcceptedIface, false); diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index b84e25a..48a4b74 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -710,20 +710,56 @@ public class PackageManagerService extends IPackageManager.Stub { res.removedInfo.sendBroadcast(false, true); Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, res.uid); + // Determine the set of users who are adding this + // package for the first time vs. those who are seeing + // an update. + int[] firstUsers; + int[] updateUsers = new int[0]; + if (res.origUsers == null || res.origUsers.length == 0) { + firstUsers = res.newUsers; + } else { + firstUsers = new int[0]; + for (int i=0; i<res.newUsers.length; i++) { + int user = res.newUsers[i]; + boolean isNew = true; + for (int j=0; j<res.origUsers.length; j++) { + if (res.origUsers[j] == user) { + isNew = false; + break; + } + } + if (isNew) { + int[] newFirst = new int[firstUsers.length+1]; + System.arraycopy(firstUsers, 0, newFirst, 0, + firstUsers.length); + newFirst[firstUsers.length] = user; + firstUsers = newFirst; + } else { + int[] newUpdate = new int[updateUsers.length+1]; + System.arraycopy(updateUsers, 0, newUpdate, 0, + updateUsers.length); + newUpdate[updateUsers.length] = user; + updateUsers = newUpdate; + } + } + } + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, + res.pkg.applicationInfo.packageName, + extras, null, null, firstUsers); final boolean update = res.removedInfo.removedPackage != null; if (update) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, res.pkg.applicationInfo.packageName, - extras, null, null, res.users); + extras, null, null, updateUsers); if (update) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, res.pkg.applicationInfo.packageName, - extras, null, null, res.users); + extras, null, null, updateUsers); sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, - res.pkg.applicationInfo.packageName, null, res.users); + res.pkg.applicationInfo.packageName, null, updateUsers); } if (res.removedInfo.args != null) { // Remove the replaced package's older resources safely now @@ -1782,7 +1818,7 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.publicSourceDir = ps.resourcePathString; pkg.applicationInfo.sourceDir = ps.codePathString; pkg.applicationInfo.dataDir = - getDataPathForPackage(ps.pkg.packageName, 0).getPath(); + getDataPathForPackage(packageName, 0).getPath(); pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; } // pkg.mSetEnabled = ps.getEnabled(userId); @@ -5278,6 +5314,7 @@ public class PackageManagerService extends IPackageManager.Stub { return pkgs.get(0); } } + mSettings.mPackagesToBeCleaned.remove(userId); } // Move on to the next user to clean. long ident = Binder.clearCallingIdentity(); @@ -5343,8 +5380,10 @@ public class PackageManagerService extends IPackageManager.Stub { public void onEvent(int event, String path) { String removedPackage = null; int removedUid = -1; + int[] removedUsers = null; String addedPackage = null; int addedUid = -1; + int[] addedUsers = null; // TODO post a message to the handler to obtain serial ordering synchronized (mInstallLock) { @@ -5373,6 +5412,15 @@ public class PackageManagerService extends IPackageManager.Stub { // reader synchronized (mPackages) { p = mAppDirs.get(fullPathStr); + if (p != null) { + PackageSetting ps = mSettings.mPackages.get(p.applicationInfo.packageName); + if (ps != null) { + removedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); + } else { + removedUsers = sUserManager.getUserIds(); + } + } + addedUsers = sUserManager.getUserIds(); } if ((event&REMOVE_EVENTS) != 0) { if (p != null) { @@ -5390,7 +5438,7 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.PARSE_CHATTY | PackageParser.PARSE_MUST_BE_APK, SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME, - System.currentTimeMillis(), null); + System.currentTimeMillis(), UserHandle.ALL); if (p != null) { /* * TODO this seems dangerous as the package may have @@ -5419,13 +5467,13 @@ public class PackageManagerService extends IPackageManager.Stub { extras.putInt(Intent.EXTRA_UID, removedUid); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false); sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, - extras, null, null, null); + extras, null, null, removedUsers); } if (addedPackage != null) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, addedUid); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage, - extras, null, null, null); + extras, null, null, addedUsers); } } @@ -5468,7 +5516,7 @@ public class PackageManagerService extends IPackageManager.Stub { if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) { user = UserHandle.ALL; } else { - user = Process.myUserHandle(); + user = new UserHandle(UserHandle.getUserId(uid)); } final int filteredFlags; @@ -5531,6 +5579,10 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public void verifyPendingInstall(int id, int verificationCode) throws RemoteException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, + "Only package verification agents can verify applications"); + final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED); final PackageVerificationResponse response = new PackageVerificationResponse( verificationCode, Binder.getCallingUid()); @@ -5542,6 +5594,10 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, + "Only package verification agents can extend verification timeouts"); + final PackageVerificationState state = mPendingVerification.get(id); final PackageVerificationResponse response = new PackageVerificationResponse( verificationCodeAtTimeout, Binder.getCallingUid()); @@ -7212,7 +7268,10 @@ public class PackageManagerService extends IPackageManager.Stub { class PackageInstalledInfo { String name; int uid; - int[] users; + // The set of users that originally had this package installed. + int[] origUsers; + // The set of users that now have this package installed. + int[] newUsers; PackageParser.Package pkg; int returnCode; PackageRemovedInfo removedInfo; @@ -7362,7 +7421,7 @@ public class PackageManagerService extends IPackageManager.Stub { int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME; if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode, - origUpdateTime, user) == null) { + origUpdateTime, null) == null) { Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade"); return; } @@ -7511,10 +7570,6 @@ public class PackageManagerService extends IPackageManager.Stub { UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0)); res.name = pkgName; - PackageSetting ps = mSettings.mPackages.get(pkgName); - if (ps != null) { - res.users = ps.getInstalledUsers(sUserManager.getUserIds()); - } res.uid = newPackage.applicationInfo.uid; res.pkg = newPackage; mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE); @@ -7612,6 +7667,7 @@ public class PackageManagerService extends IPackageManager.Stub { systemApp = (ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } + res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); } } @@ -7634,12 +7690,12 @@ public class PackageManagerService extends IPackageManager.Stub { installerPackageName, res); } else { installNewPackageLI(pkg, parseFlags, scanMode, args.user, - installerPackageName,res); + installerPackageName, res); } synchronized (mPackages) { - PackageSetting ps = mSettings.mPackages.get(pkgName); + final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { - res.users = ps.getInstalledUsers(sUserManager.getUserIds()); + res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); } } } @@ -7865,7 +7921,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (outInfo != null) { outInfo.removedPackage = packageName; outInfo.removedUsers = deletedPs != null - ? deletedPs.getInstalledUsers(sUserManager.getUserIds()) : null; + ? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true) + : null; } } if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) { diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java index 6d31f0e..d8f7345 100644 --- a/services/java/com/android/server/pm/PackageSettingBase.java +++ b/services/java/com/android/server/pm/PackageSettingBase.java @@ -210,17 +210,17 @@ class PackageSettingBase extends GrantedPermissions { return false; } - int[] getInstalledUsers(int[] users) { + int[] queryInstalledUsers(int[] users, boolean installed) { int num = 0; for (int user : users) { - if (getInstalled(user)) { + if (getInstalled(user) == installed) { num++; } } int[] res = new int[num]; num = 0; for (int user : users) { - if (getInstalled(user)) { + if (getInstalled(user) == installed) { res[num] = user; num++; } diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index 750aa72..fb04d0f 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -23,6 +23,8 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.IStopUserCallback; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -33,6 +35,7 @@ import android.os.FileUtils; import android.os.IUserManager; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.os.RemoteException; import android.os.UserHandle; import android.util.AtomicFile; import android.util.Slog; @@ -549,13 +552,36 @@ public class UserManagerService extends IUserManager.Stub { */ public boolean removeUser(int userHandle) { checkManageUsersPermission("Only the system can remove users"); + final UserInfo user; + synchronized (mPackagesLock) { + user = mUsers.get(userHandle); + if (userHandle == 0 || user == null) { + return false; + } + } + + int res; + try { + res = ActivityManagerNative.getDefault().stopUser(userHandle, + new IStopUserCallback.Stub() { + @Override + public void userStopped(int userId) { + finishRemoveUser(userId); + } + @Override + public void userStopAborted(int userId) { + } + }); + } catch (RemoteException e) { + return false; + } + + return res == ActivityManager.USER_OP_SUCCESS; + } + + void finishRemoveUser(int userHandle) { synchronized (mInstallLock) { synchronized (mPackagesLock) { - final UserInfo user = mUsers.get(userHandle); - if (userHandle == 0 || user == null) { - return false; - } - // Cleanup package manager settings mPm.cleanUpUserLILPw(userHandle); @@ -574,7 +600,6 @@ public class UserManagerService extends IUserManager.Stub { Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS); - return true; } @Override diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java index cd211da..6b6d899 100644 --- a/services/java/com/android/server/power/DisplayPowerController.java +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -45,7 +45,6 @@ import android.view.Display; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; /** @@ -169,6 +168,9 @@ final class DisplayPowerController { // The twilight service. private final TwilightService mTwilight; + // The display manager. + private final DisplayManager mDisplayManager; + // The sensor manager. private final SensorManager mSensorManager; @@ -330,6 +332,7 @@ final class DisplayPowerController { mLights = lights; mTwilight = twilight; mSensorManager = new SystemSensorManager(mHandler.getLooper()); + mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); final Resources resources = context.getResources(); mScreenBrightnessDimConfig = resources.getInteger( @@ -475,7 +478,7 @@ final class DisplayPowerController { private void initialize() { final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR; - Display display = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); + Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); mPowerState = new DisplayPowerState(new ElectronBeam(display), new PhotonicModulator(executor, mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT), @@ -980,7 +983,7 @@ final class DisplayPowerController { } }; - public void dump(PrintWriter pw) { + public void dump(final PrintWriter pw) { synchronized (mLock) { pw.println(); pw.println("Display Controller Locked State:"); @@ -1000,33 +1003,12 @@ final class DisplayPowerController { pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline); pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig); - if (Looper.myLooper() == mHandler.getLooper()) { - dumpLocal(pw); - } else { - final StringWriter out = new StringWriter(); - final CountDownLatch latch = new CountDownLatch(1); - Message msg = Message.obtain(mHandler, new Runnable() { - @Override - public void run() { - PrintWriter localpw = new PrintWriter(out); - try { - dumpLocal(localpw); - } finally { - localpw.flush(); - latch.countDown(); - } - } - }); - msg.setAsynchronous(true); - mHandler.sendMessage(msg); - try { - latch.await(); - pw.print(out.toString()); - } catch (InterruptedException ex) { - pw.println(); - pw.println("Failed to dump thread state due to interrupted exception!"); + mHandler.runWithScissors(new Runnable() { + @Override + public void run() { + dumpLocal(pw); } - } + }); } private void dumpLocal(PrintWriter pw) { diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java index aad5a9a..0c68997 100644 --- a/services/java/com/android/server/power/ElectronBeam.java +++ b/services/java/com/android/server/power/ElectronBeam.java @@ -481,8 +481,8 @@ final class ElectronBeam { try { if (mSurface == null) { try { - mSurface = new Surface(mSurfaceSession, Process.myPid(), - "ElectronBeam", mDisplayLayerStack, mDisplayWidth, mDisplayHeight, + mSurface = new Surface(mSurfaceSession, + "ElectronBeam", mDisplayWidth, mDisplayHeight, PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN); } catch (Surface.OutOfResourcesException ex) { Slog.e(TAG, "Unable to create surface.", ex); @@ -490,6 +490,7 @@ final class ElectronBeam { } } + mSurface.setLayerStack(mDisplayLayerStack); mSurface.setSize(mDisplayWidth, mDisplayHeight); switch (mDisplayRotation) { diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index 6d68104..59d0954 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -1136,7 +1136,8 @@ public final class PowerManagerService extends IPowerManager.Stub private boolean isItBedTimeYetLocked() { return mBootCompleted && !mStayOn && (mWakeLockSummary - & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) == 0 + & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM + | WAKE_LOCK_PROXIMITY_SCREEN_OFF)) == 0 && (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) == 0; } diff --git a/services/java/com/android/server/usb/UsbDebuggingManager.java b/services/java/com/android/server/usb/UsbDebuggingManager.java index a3b45c7..1bb3a2c 100644 --- a/services/java/com/android/server/usb/UsbDebuggingManager.java +++ b/services/java/com/android/server/usb/UsbDebuggingManager.java @@ -53,16 +53,15 @@ public class UsbDebuggingManager implements Runnable { private final int BUFFER_SIZE = 4096; private final Context mContext; - private final Thread mThread; private final Handler mHandler; private final HandlerThread mHandlerThread; + private Thread mThread; private boolean mAdbEnabled = false; private String mFingerprints; private LocalSocket mSocket = null; private OutputStream mOutputStream = null; public UsbDebuggingManager(Context context) { - mThread = new Thread(this); mHandlerThread = new HandlerThread("UsbDebuggingHandler"); mHandlerThread.start(); mHandler = new UsbDebuggingHandler(mHandlerThread.getLooper()); @@ -165,6 +164,7 @@ public class UsbDebuggingManager implements Runnable { mAdbEnabled = true; + mThread = new Thread(UsbDebuggingManager.this); mThread.start(); break; @@ -181,8 +181,10 @@ public class UsbDebuggingManager implements Runnable { } catch (Exception ex) { } + mThread = null; mOutputStream = null; mSocket = null; + break; case MESSAGE_ADB_ALLOW: { String key = (String)msg.obj; diff --git a/services/java/com/android/server/wm/BlackFrame.java b/services/java/com/android/server/wm/BlackFrame.java index 64d2602..5b77b20 100644 --- a/services/java/com/android/server/wm/BlackFrame.java +++ b/services/java/com/android/server/wm/BlackFrame.java @@ -43,14 +43,15 @@ public class BlackFrame { int w = r-l; int h = b-t; if (WindowManagerService.DEBUG_SURFACE_TRACE) { - surface = new WindowStateAnimator.SurfaceTrace(session, 0, "BlackSurface(" - + l + ", " + t + ")", layerStack, - w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); + surface = new WindowStateAnimator.SurfaceTrace(session, "BlackSurface(" + + l + ", " + t + ")", + w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM | Surface.HIDDEN); } else { - surface = new Surface(session, 0, "BlackSurface", layerStack, - w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); + surface = new Surface(session, "BlackSurface", + w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM | Surface.HIDDEN); } surface.setAlpha(1); + surface.setLayerStack(layerStack); surface.setLayer(layer); surface.show(); if (WindowManagerService.SHOW_TRANSACTIONS || diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java index 81daac6..afcf339 100644 --- a/services/java/com/android/server/wm/DimAnimator.java +++ b/services/java/com/android/server/wm/DimAnimator.java @@ -43,20 +43,21 @@ class DimAnimator { if (mDimSurface == null) { try { if (WindowManagerService.DEBUG_SURFACE_TRACE) { - mDimSurface = new WindowStateAnimator.SurfaceTrace(session, 0, + mDimSurface = new WindowStateAnimator.SurfaceTrace(session, "DimAnimator", - layerStack, 16, 16, PixelFormat.OPAQUE, - Surface.FX_SURFACE_DIM); + 16, 16, PixelFormat.OPAQUE, + Surface.FX_SURFACE_DIM | Surface.HIDDEN); } else { - mDimSurface = new Surface(session, 0, - "DimAnimator", - layerStack, 16, 16, PixelFormat.OPAQUE, - Surface.FX_SURFACE_DIM); + mDimSurface = new Surface(session, "DimAnimator", + 16, 16, PixelFormat.OPAQUE, + Surface.FX_SURFACE_DIM | Surface.HIDDEN); } if (WindowManagerService.SHOW_TRANSACTIONS || WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + ": CREATE"); + mDimSurface.setLayerStack(layerStack); mDimSurface.setAlpha(0.0f); + mDimSurface.show(); } catch (Exception e) { Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e); } @@ -212,4 +213,4 @@ class DimAnimator { mDimTarget = dimTarget; } } -}
\ No newline at end of file +} diff --git a/services/java/com/android/server/wm/DimSurface.java b/services/java/com/android/server/wm/DimSurface.java index 4ab8ce1..ddbd70d 100644 --- a/services/java/com/android/server/wm/DimSurface.java +++ b/services/java/com/android/server/wm/DimSurface.java @@ -34,20 +34,21 @@ class DimSurface { if (mDimSurface == null) { try { if (WindowManagerService.DEBUG_SURFACE_TRACE) { - mDimSurface = new WindowStateAnimator.SurfaceTrace(session, 0, + mDimSurface = new WindowStateAnimator.SurfaceTrace(session, "DimSurface", - layerStack, 16, 16, PixelFormat.OPAQUE, - Surface.FX_SURFACE_DIM); + 16, 16, PixelFormat.OPAQUE, + Surface.FX_SURFACE_DIM | Surface.HIDDEN); } else { - mDimSurface = new Surface(session, 0, - "DimSurface", - layerStack, 16, 16, PixelFormat.OPAQUE, - Surface.FX_SURFACE_DIM); + mDimSurface = new Surface(session, "DimSurface", + 16, 16, PixelFormat.OPAQUE, + Surface.FX_SURFACE_DIM | Surface.HIDDEN); } if (WindowManagerService.SHOW_TRANSACTIONS || WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + ": CREATE"); + mDimSurface.setLayerStack(layerStack); mDimSurface.setAlpha(0.0f); + mDimSurface.show(); } catch (Exception e) { Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e); } diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java index 7679413..acf3249 100644 --- a/services/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java @@ -215,12 +215,12 @@ class ScreenRotationAnimation { try { try { if (WindowManagerService.DEBUG_SURFACE_TRACE) { - mSurface = new SurfaceTrace(session, 0, "FreezeSurface", - mDisplay.getLayerStack(), mWidth, mHeight, + mSurface = new SurfaceTrace(session, "FreezeSurface", + mWidth, mHeight, PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN); } else { - mSurface = new Surface(session, 0, "FreezeSurface", - mDisplay.getLayerStack(), mWidth, mHeight, + mSurface = new Surface(session, "FreezeSurface", + mWidth, mHeight, PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN); } if (!mSurface.isValid()) { @@ -228,6 +228,7 @@ class ScreenRotationAnimation { mSurface = null; return; } + mSurface.setLayerStack(mDisplay.getLayerStack()); mSurface.setLayer(FREEZE_LAYER + 1); mSurface.setAlpha(0); mSurface.show(); diff --git a/services/java/com/android/server/wm/StrictModeFlash.java b/services/java/com/android/server/wm/StrictModeFlash.java index 775aa0f..90bbd08 100644 --- a/services/java/com/android/server/wm/StrictModeFlash.java +++ b/services/java/com/android/server/wm/StrictModeFlash.java @@ -37,14 +37,16 @@ class StrictModeFlash { public StrictModeFlash(Display display, SurfaceSession session) { try { - mSurface = new Surface(session, 0, "StrictModeFlash", display.getLayerStack(), - 1, 1, PixelFormat.TRANSLUCENT, 0); + mSurface = new Surface(session, "StrictModeFlash", + 1, 1, PixelFormat.TRANSLUCENT, Surface.HIDDEN); } catch (Surface.OutOfResourcesException e) { return; } + mSurface.setLayerStack(display.getLayerStack()); mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary. mSurface.setPosition(0, 0); + mSurface.show(); mDrawNeeded = true; } diff --git a/services/java/com/android/server/wm/Watermark.java b/services/java/com/android/server/wm/Watermark.java index 5901cc8..ac152c9 100644 --- a/services/java/com/android/server/wm/Watermark.java +++ b/services/java/com/android/server/wm/Watermark.java @@ -113,9 +113,9 @@ class Watermark { mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor); try { - mSurface = new Surface(session, 0, - "WatermarkSurface", mDisplay.getLayerStack(), - 1, 1, PixelFormat.TRANSLUCENT, 0); + mSurface = new Surface(session, "WatermarkSurface", + 1, 1, PixelFormat.TRANSLUCENT, Surface.HIDDEN); + mSurface.setLayerStack(mDisplay.getLayerStack()); mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100); mSurface.setPosition(0, 0); mSurface.show(); @@ -174,4 +174,4 @@ class Watermark { } } } -}
\ No newline at end of file +} diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 42bc7ce..0d9db90 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -739,116 +739,38 @@ public class WindowManagerService extends IWindowManager.Stub // For example, when this flag is true, there will be no wallpaper service. final boolean mOnlyCore; - public static WindowManagerService main(Context context, - PowerManagerService pm, DisplayManagerService dm, - boolean haveInputMethods, boolean allowBootMsgs, - boolean onlyCore) { - WMThread thr = new WMThread(context, pm, dm, haveInputMethods, allowBootMsgs, onlyCore); - thr.start(); - - synchronized (thr) { - while (thr.mService == null) { - try { - thr.wait(); - } catch (InterruptedException e) { - } + public static WindowManagerService main(final Context context, + final PowerManagerService pm, final DisplayManagerService dm, + final Handler uiHandler, final Handler wmHandler, + final boolean haveInputMethods, final boolean showBootMsgs, + final boolean onlyCore) { + final WindowManagerService[] holder = new WindowManagerService[1]; + wmHandler.runWithScissors(new Runnable() { + @Override + public void run() { + holder[0] = new WindowManagerService(context, pm, dm, + uiHandler, haveInputMethods, showBootMsgs, onlyCore); } - return thr.mService; - } - } - - static class WMThread extends Thread { - WindowManagerService mService; - - private final Context mContext; - private final PowerManagerService mPM; - private final DisplayManagerService mDisplayManager; - private final boolean mHaveInputMethods; - private final boolean mAllowBootMessages; - private final boolean mOnlyCore; - - public WMThread(Context context, PowerManagerService pm, - DisplayManagerService dm, - boolean haveInputMethods, boolean allowBootMsgs, boolean onlyCore) { - super("WindowManager"); - mContext = context; - mPM = pm; - mDisplayManager = dm; - mHaveInputMethods = haveInputMethods; - mAllowBootMessages = allowBootMsgs; - mOnlyCore = onlyCore; - } - - @Override - public void run() { - Looper.prepare(); - //Looper.myLooper().setMessageLogging(new LogPrinter( - // android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM)); - WindowManagerService s = new WindowManagerService(mContext, mPM, mDisplayManager, - mHaveInputMethods, mAllowBootMessages, mOnlyCore); - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_DISPLAY); - android.os.Process.setCanSelfBackground(false); - - synchronized (this) { - mService = s; - notifyAll(); - } - - // For debug builds, log event loop stalls to dropbox for analysis. - if (StrictMode.conditionallyEnableDebugLogging()) { - Slog.i(TAG, "Enabled StrictMode logging for WMThread's Looper"); - } - - Looper.loop(); - } + }); + return holder[0]; } - static class PolicyThread extends Thread { - private final WindowManagerPolicy mPolicy; - private final WindowManagerService mService; - private final Context mContext; - boolean mRunning = false; + private void initPolicy(Handler uiHandler) { + uiHandler.runWithScissors(new Runnable() { + @Override + public void run() { + WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); - public PolicyThread(WindowManagerPolicy policy, - WindowManagerService service, Context context) { - super("WindowManagerPolicy"); - mPolicy = policy; - mService = service; - mContext = context; - } - - @Override - public void run() { - Looper.prepare(); - WindowManagerPolicyThread.set(this, Looper.myLooper()); - - //Looper.myLooper().setMessageLogging(new LogPrinter( - // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM)); - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_FOREGROUND); - android.os.Process.setCanSelfBackground(false); - mPolicy.init(mContext, mService, mService); - mService.mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer() - * TYPE_LAYER_MULTIPLIER - + TYPE_LAYER_OFFSET; - - synchronized (this) { - mRunning = true; - notifyAll(); - } - - // For debug builds, log event loop stalls to dropbox for analysis. - if (StrictMode.conditionallyEnableDebugLogging()) { - Slog.i(TAG, "Enabled StrictMode for PolicyThread's Looper"); + mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); + mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer() + * TYPE_LAYER_MULTIPLIER + + TYPE_LAYER_OFFSET; } - - Looper.loop(); - } + }); } private WindowManagerService(Context context, PowerManagerService pm, - DisplayManagerService displayManager, + DisplayManagerService displayManager, Handler uiHandler, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) { mContext = context; mHaveInputMethods = haveInputMethods; @@ -857,7 +779,7 @@ public class WindowManagerService extends IWindowManager.Stub mLimitedAlphaCompositing = context.getResources().getBoolean( com.android.internal.R.bool.config_sf_limitedAlpha); mDisplayManagerService = displayManager; - mDisplayManager = DisplayManager.getInstance(); + mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); mHeadless = displayManager.isHeadless(); mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy); @@ -895,17 +817,7 @@ public class WindowManagerService extends IWindowManager.Stub mFxSession = new SurfaceSession(); mAnimator = new WindowAnimator(this); - PolicyThread thr = new PolicyThread(mPolicy, this, context); - thr.start(); - - synchronized (thr) { - while (!thr.mRunning) { - try { - thr.wait(); - } catch (InterruptedException e) { - } - } - } + initPolicy(uiHandler); mInputManager.start(); @@ -5847,7 +5759,8 @@ public class WindowManagerService extends IWindowManager.Stub updateLayoutToAnimationLocked(); } } - Surface.setOrientation(0, rotation); + mDisplayManagerService.setDisplayOrientation( + displayContent.getDisplayId(), rotation); } finally { if (!inTransaction) { Surface.closeTransaction(); @@ -6561,7 +6474,8 @@ public class WindowManagerService extends IWindowManager.Stub displayInfo.appHeight = appHeight; displayInfo.getLogicalMetrics(mRealDisplayMetrics, null); displayInfo.getAppMetrics(mDisplayMetrics, null); - mDisplayManagerService.setDisplayInfo(displayContent.getDisplayId(), displayInfo); + mDisplayManagerService.setDisplayInfoOverrideFromWindowManager( + displayContent.getDisplayId(), displayInfo); mAnimator.setDisplayDimensions(dw, dh, appWidth, appHeight); } @@ -6711,9 +6625,9 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { try { if (mDragState == null) { - Surface surface = new Surface(session, callerPid, "drag surface", - mDefaultDisplay.getLayerStack(), + Surface surface = new Surface(session, "drag surface", width, height, PixelFormat.TRANSLUCENT, Surface.HIDDEN); + surface.setLayerStack(mDefaultDisplay.getLayerStack()); if (SHOW_TRANSACTIONS) Slog.i(TAG, " DRAG " + surface + ": CREATE"); outSurface.copyFrom(surface); @@ -6913,7 +6827,10 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(displayContent.mDisplaySizeLock) { // Bootstrap the default logical display from the display manager. displayInfo = displayContent.getDisplayInfo(); - mDisplayManagerService.getDisplayInfo(displayId, displayInfo); + DisplayInfo newDisplayInfo = mDisplayManagerService.getDisplayInfo(displayId); + if (newDisplayInfo != null) { + displayInfo.copyFrom(newDisplayInfo); + } displayContent.mInitialDisplayWidth = displayInfo.logicalWidth; displayContent.mInitialDisplayHeight = displayInfo.logicalHeight; displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi; @@ -8342,10 +8259,11 @@ public class WindowManagerService extends IWindowManager.Stub Rect dirty = new Rect(0, 0, mNextAppTransitionThumbnail.getWidth(), mNextAppTransitionThumbnail.getHeight()); try { - Surface surface = new Surface(mFxSession, Process.myPid(), - "thumbnail anim", mDefaultDisplay.getLayerStack(), + Surface surface = new Surface(mFxSession, + "thumbnail anim", dirty.width(), dirty.height(), PixelFormat.TRANSLUCENT, Surface.HIDDEN); + surface.setLayerStack(mDefaultDisplay.getLayerStack()); topOpeningApp.mAppAnimator.thumbnail = surface; if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL " + surface + ": CREATE"); @@ -10363,7 +10281,7 @@ public class WindowManagerService extends IWindowManager.Stub public DisplayContent getDisplayContent(final int displayId) { DisplayContent displayContent = mDisplayContents.get(displayId); if (displayContent == null) { - displayContent = new DisplayContent(mDisplayManager.getRealDisplay(displayId)); + displayContent = new DisplayContent(mDisplayManager.getDisplay(displayId)); mDisplayContents.put(displayId, displayContent); } return displayContent; diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java index 982f60d..1bda22a 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -481,9 +481,9 @@ class WindowStateAnimator { private String mName; public SurfaceTrace(SurfaceSession s, - int pid, String name, int layerStack, int w, int h, int format, int flags) + String name, int w, int h, int format, int flags) throws OutOfResourcesException { - super(s, pid, name, layerStack, w, h, format, flags); + super(s, name, w, h, format, flags); mName = name != null ? name : "Not named"; mSize.set(w, h); Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by " @@ -608,7 +608,7 @@ class WindowStateAnimator { mService.makeWindowFreezingScreenIfNeededLocked(mWin); - int flags = 0; + int flags = Surface.HIDDEN; final WindowManager.LayoutParams attrs = mWin.mAttrs; if ((attrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) { @@ -652,14 +652,14 @@ class WindowStateAnimator { } if (DEBUG_SURFACE_TRACE) { mSurface = new SurfaceTrace( - mSession.mSurfaceSession, mSession.mPid, + mSession.mSurfaceSession, attrs.getTitle().toString(), - mLayerStack, w, h, format, flags); + w, h, format, flags); } else { mSurface = new Surface( - mSession.mSurfaceSession, mSession.mPid, + mSession.mSurfaceSession, attrs.getTitle().toString(), - mLayerStack, w, h, format, flags); + w, h, format, flags); } mWin.mHasSurface = true; if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG, @@ -701,10 +701,10 @@ class WindowStateAnimator { mSurfaceY = mWin.mFrame.top + mWin.mYOffset; mSurface.setPosition(mSurfaceX, mSurfaceY); mSurfaceLayer = mAnimLayer; + mSurface.setLayerStack(mLayerStack); mSurface.setLayer(mAnimLayer); mSurface.setAlpha(0); mSurfaceShown = false; - mSurface.hide(); } catch (RuntimeException e) { Slog.w(TAG, "Error creating surface in " + w, e); mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true); |
