summaryrefslogtreecommitdiffstats
path: root/services/java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java')
-rw-r--r--services/java/com/android/server/AlarmManagerService.java32
-rw-r--r--services/java/com/android/server/ConnectivityService.java84
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java29
-rw-r--r--services/java/com/android/server/SystemServer.java62
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java20
-rw-r--r--services/java/com/android/server/Watchdog.java60
-rw-r--r--services/java/com/android/server/WifiService.java25
-rw-r--r--services/java/com/android/server/am/ActiveServices.java13
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java422
-rwxr-xr-xservices/java/com/android/server/am/ActivityStack.java33
-rw-r--r--services/java/com/android/server/am/BroadcastQueue.java24
-rw-r--r--services/java/com/android/server/am/BroadcastRecord.java24
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java12
-rw-r--r--services/java/com/android/server/am/UserStartedState.java43
-rw-r--r--services/java/com/android/server/display/DisplayAdapter.java79
-rw-r--r--services/java/com/android/server/display/DisplayDevice.java38
-rw-r--r--services/java/com/android/server/display/DisplayDeviceInfo.java20
-rw-r--r--services/java/com/android/server/display/DisplayManagerService.java536
-rw-r--r--services/java/com/android/server/display/HeadlessDisplayAdapter.java30
-rw-r--r--services/java/com/android/server/display/LocalDisplayAdapter.java74
-rw-r--r--services/java/com/android/server/display/OverlayDisplayAdapter.java530
-rw-r--r--services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java58
-rw-r--r--services/java/com/android/server/net/LockdownVpnTracker.java37
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java93
-rw-r--r--services/java/com/android/server/pm/PackageSettingBase.java6
-rw-r--r--services/java/com/android/server/pm/UserManagerService.java37
-rw-r--r--services/java/com/android/server/power/DisplayPowerController.java40
-rw-r--r--services/java/com/android/server/power/ElectronBeam.java5
-rw-r--r--services/java/com/android/server/power/PowerManagerService.java3
-rw-r--r--services/java/com/android/server/usb/UsbDebuggingManager.java6
-rw-r--r--services/java/com/android/server/wm/BlackFrame.java11
-rw-r--r--services/java/com/android/server/wm/DimAnimator.java17
-rw-r--r--services/java/com/android/server/wm/DimSurface.java15
-rw-r--r--services/java/com/android/server/wm/ScreenRotationAnimation.java9
-rw-r--r--services/java/com/android/server/wm/StrictModeFlash.java6
-rw-r--r--services/java/com/android/server/wm/Watermark.java8
-rwxr-xr-xservices/java/com/android/server/wm/WindowManagerService.java162
-rw-r--r--services/java/com/android/server/wm/WindowStateAnimator.java16
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);