diff options
77 files changed, 2047 insertions, 1276 deletions
diff --git a/api/current.txt b/api/current.txt index d5e585df6802..177b78705968 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12745,14 +12745,12 @@ package android.net { method public static javax.net.ssl.SSLSocketFactory getDefault(int, android.net.SSLSessionCache); method public java.lang.String[] getDefaultCipherSuites(); method public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache); - method public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, int, android.net.SSLSessionCache); method public static javax.net.ssl.SSLSocketFactory getInsecure(int, android.net.SSLSessionCache); method public byte[] getNpnSelectedProtocol(java.net.Socket); method public java.lang.String[] getSupportedCipherSuites(); method public void setHostname(java.net.Socket, java.lang.String); method public void setKeyManagers(javax.net.ssl.KeyManager[]); method public void setNpnProtocols(byte[][]); - method public void setSoWriteTimeout(java.net.Socket, int) throws java.net.SocketException; method public void setTrustManagers(javax.net.ssl.TrustManager[]); method public void setUseSessionTickets(java.net.Socket, boolean); } @@ -20332,7 +20330,6 @@ package android.service.dreams { method public boolean isInteractive(); method public boolean isLowProfile(); method public boolean isScreenBright(); - method protected deprecated void lightsOut(); method public void onActionModeFinished(android.view.ActionMode); method public void onActionModeStarted(android.view.ActionMode); method public void onAttachedToWindow(); @@ -20360,7 +20357,7 @@ package android.service.dreams { field public static final java.lang.String ACTION_DREAMING_STARTED = "android.intent.action.DREAMING_STARTED"; field public static final java.lang.String ACTION_DREAMING_STOPPED = "android.intent.action.DREAMING_STOPPED"; field public static final java.lang.String CATEGORY_DREAM = "android.intent.category.DREAM"; - field public static final java.lang.String METADATA_NAME_CONFIG_ACTIVITY = "android.service.dreams.config_activity"; + field public static final java.lang.String DREAM_META_DATA = "android.service.dream"; } } diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index bc9e74ed4c27..396b32fb7508 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -804,8 +804,9 @@ public class Am { ParcelFileDescriptor fd = null; try { - fd = ParcelFileDescriptor.open( - new File(heapFile), + File file = new File(heapFile); + file.delete(); + fd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE | ParcelFileDescriptor.MODE_READ_WRITE); diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 9b08493f0379..9874b0b36caf 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1509,9 +1509,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case DUMP_HEAP_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String process = data.readString(); + int userId = data.readInt(); boolean managed = data.readInt() != 0; String path = data.readString(); - int userId = data.readInt(); ParcelFileDescriptor fd = data.readInt() != 0 ? data.readFileDescriptor() : null; boolean res = dumpHeap(process, userId, managed, path, fd); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d4b204f8528e..6638433e741a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2711,7 +2711,7 @@ public final class ActivityThread { r.activity.performResume(); EventLog.writeEvent(LOG_ON_RESUME_CALLED, - r.activity.getComponentName().getClassName()); + UserHandle.myUserId(), r.activity.getComponentName().getClassName()); r.paused = false; r.stopped = false; @@ -2979,7 +2979,8 @@ public final class ActivityThread { // Now we are idle. r.activity.mCalled = false; mInstrumentation.callActivityOnPause(r.activity); - EventLog.writeEvent(LOG_ON_PAUSE_CALLED, r.activity.getComponentName().getClassName()); + EventLog.writeEvent(LOG_ON_PAUSE_CALLED, UserHandle.myUserId(), + r.activity.getComponentName().getClassName()); if (!r.activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + @@ -3364,7 +3365,7 @@ public final class ActivityThread { try { r.activity.mCalled = false; mInstrumentation.callActivityOnPause(r.activity); - EventLog.writeEvent(LOG_ON_PAUSE_CALLED, + EventLog.writeEvent(LOG_ON_PAUSE_CALLED, UserHandle.myUserId(), r.activity.getComponentName().getClassName()); if (!r.activity.mCalled) { throw new SuperNotCalledException( diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index c095280e57d4..0acad759501f 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -124,6 +124,9 @@ public class NotificationManager int[] idOut = new int[1]; INotificationManager service = getService(); String pkg = mContext.getPackageName(); + if (notification.sound != null) { + notification.sound = notification.sound.getCanonicalUri(); + } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); try { service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut, @@ -143,6 +146,9 @@ public class NotificationManager int[] idOut = new int[1]; INotificationManager service = getService(); String pkg = mContext.getPackageName(); + if (notification.sound != null) { + notification.sound = notification.sound.getCanonicalUri(); + } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); try { service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut, diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ac36cf714f11..201b43f43728 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -178,7 +178,7 @@ public abstract class Context { * Flag for {@link #bindService}: indicates that the client application * binding to this service considers the service to be more important than * the app itself. When set, the platform will try to have the out of - * memory kill the app before it kills the service it is bound to, though + * memory killer kill the app before it kills the service it is bound to, though * this is not guaranteed to be the case. */ public static final int BIND_ABOVE_CLIENT = 0x0008; @@ -219,6 +219,19 @@ public abstract class Context { public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080; /** + * @hide An idea that is not yet implemented. + * Flag for {@link #bindService}: If binding from an activity, consider + * this service to be visible like the binding activity is. That is, + * it will be treated as something more important to keep around than + * invisible background activities. This will impact the number of + * recent activities the user can switch between without having them + * restart. There is no guarantee this will be respected, as the system + * tries to balance such requests from one app vs. the importantance of + * keeping other apps around. + */ + public static final int BIND_VISIBLE = 0x0100; + + /** * Flag for {@link #bindService}: Don't consider the bound service to be * visible, even if the caller is visible. * @hide diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index 42fb5c3cd682..846443d6d235 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -90,7 +90,6 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { private byte[] mNpnProtocols = null; private final int mHandshakeTimeoutMillis; - private final int mWriteTimeoutMillis; private final SSLClientSessionCache mSessionCache; private final boolean mSecure; @@ -101,21 +100,12 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } private SSLCertificateSocketFactory( - int handshakeTimeoutMillis, - int writeTimeoutMillis, - SSLSessionCache cache, - boolean secure) { + int handshakeTimeoutMillis, SSLSessionCache cache, boolean secure) { mHandshakeTimeoutMillis = handshakeTimeoutMillis; - mWriteTimeoutMillis = writeTimeoutMillis; mSessionCache = cache == null ? null : cache.mSessionCache; mSecure = secure; } - private SSLCertificateSocketFactory( - int handshakeTimeoutMillis, SSLSessionCache cache, boolean secure) { - this(handshakeTimeoutMillis, 0, cache, secure); - } - /** * Returns a new socket factory instance with an optional handshake timeout. * @@ -172,24 +162,6 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } /** - * Returns a socket factory (also named SSLSocketFactory, but in a different - * namespace) for use with the Apache HTTP stack. - * - * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 - * for none. The socket timeout is reset to 0 after the handshake. - * @param writeTimeoutMillis the desired write timeout in milliseconds or 0 for none. - * @param cache The {@link SSLSessionCache} to use, or null for no cache. - * @return a new SocketFactory with the specified parameters - */ - public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory( - int handshakeTimeoutMillis, - int writeTimeoutMillis, - SSLSessionCache cache) { - return new org.apache.http.conn.ssl.SSLSocketFactory(new SSLCertificateSocketFactory( - handshakeTimeoutMillis, writeTimeoutMillis, cache, true)); - } - - /** * Verify the hostname of the certificate used by the other end of a * connected socket. You MUST call this if you did not supply a hostname * to {@link #createSocket()}. It is harmless to call this method @@ -376,8 +348,10 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { * To take effect, this option must be set before the blocking method was called. * * @param socket a socket created by this factory. - * @param writeTimeoutMilliseconds the desired write timeout in milliseconds. + * @param timeout the desired write timeout in milliseconds. * @throws IllegalArgumentException if the socket was not created by this factory. + * + * @hide */ public void setSoWriteTimeout(Socket socket, int writeTimeoutMilliseconds) throws SocketException { @@ -404,7 +378,6 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close); s.setNpnProtocols(mNpnProtocols); s.setHandshakeTimeout(mHandshakeTimeoutMillis); - s.setSoWriteTimeout(mWriteTimeoutMillis); if (mSecure) { verifyHostname(s, host); } @@ -424,7 +397,6 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(); s.setNpnProtocols(mNpnProtocols); s.setHandshakeTimeout(mHandshakeTimeoutMillis); - s.setSoWriteTimeout(mWriteTimeoutMillis); return s; } @@ -442,7 +414,6 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { addr, port, localAddr, localPort); s.setNpnProtocols(mNpnProtocols); s.setHandshakeTimeout(mHandshakeTimeoutMillis); - s.setSoWriteTimeout(mWriteTimeoutMillis); return s; } @@ -458,7 +429,6 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port); s.setNpnProtocols(mNpnProtocols); s.setHandshakeTimeout(mHandshakeTimeoutMillis); - s.setSoWriteTimeout(mWriteTimeoutMillis); return s; } @@ -475,7 +445,6 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { host, port, localAddr, localPort); s.setNpnProtocols(mNpnProtocols); s.setHandshakeTimeout(mHandshakeTimeoutMillis); - s.setSoWriteTimeout(mWriteTimeoutMillis); if (mSecure) { verifyHostname(s, host); } @@ -493,7 +462,6 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port); s.setNpnProtocols(mNpnProtocols); s.setHandshakeTimeout(mHandshakeTimeoutMillis); - s.setSoWriteTimeout(mWriteTimeoutMillis); if (mSecure) { verifyHostname(s, host); } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 3b990e3f937d..cc6903dbacad 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -16,10 +16,13 @@ package android.net; +import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; +import android.os.Environment.UserEnvironment; import android.util.Log; import java.io.File; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charsets; @@ -2288,4 +2291,39 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { builder = builder.appendEncodedPath(pathSegment); return builder.build(); } + + /** + * If this {@link Uri} is {@code file://}, then resolve and return its + * canonical path. Also fixes legacy emulated storage paths so they are + * usable across user boundaries. Should always be called from the app + * process before sending elsewhere. + * + * @hide + */ + public Uri getCanonicalUri() { + if ("file".equals(getScheme())) { + final String canonicalPath; + try { + canonicalPath = new File(getPath()).getCanonicalPath(); + } catch (IOException e) { + return this; + } + + if (Environment.isExternalStorageEmulated()) { + final String legacyPath = Environment.getLegacyExternalStorageDirectory() + .toString(); + + // Splice in user-specific path when legacy path is found + if (canonicalPath.startsWith(legacyPath)) { + return Uri.fromFile(new File( + Environment.getExternalStorageDirectory().toString(), + canonicalPath.substring(legacyPath.length() + 1))); + } + } + + return Uri.fromFile(new File(canonicalPath)); + } else { + return this; + } + } } diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java index 8169a943c206..c534e58f37e1 100644 --- a/core/java/android/net/http/AndroidHttpClient.java +++ b/core/java/android/net/http/AndroidHttpClient.java @@ -16,16 +16,7 @@ package android.net.http; -import android.content.ContentResolver; -import android.content.Context; -import android.net.SSLCertificateSocketFactory; -import android.net.SSLSessionCache; -import android.os.Looper; -import android.util.Base64; -import android.util.Log; - import com.android.internal.http.HttpDateTime; - import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; @@ -34,18 +25,18 @@ import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; +import org.apache.http.entity.AbstractHttpEntity; +import org.apache.http.entity.ByteArrayEntity; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.protocol.ClientContext; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.params.HttpClientParams; -import org.apache.http.client.protocol.ClientContext; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.entity.AbstractHttpEntity; -import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.RequestWrapper; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; @@ -53,17 +44,25 @@ import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; -import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.BasicHttpProcessor; import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.BasicHttpContext; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.ByteArrayOutputStream; import java.io.OutputStream; -import java.net.URI; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; +import java.net.URI; + +import android.content.Context; +import android.content.ContentResolver; +import android.net.SSLCertificateSocketFactory; +import android.net.SSLSessionCache; +import android.os.Looper; +import android.util.Base64; +import android.util.Log; /** * Implementation of the Apache {@link DefaultHttpClient} that is configured with @@ -135,7 +134,7 @@ public final class AndroidHttpClient implements HttpClient { PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", SSLCertificateSocketFactory.getHttpSocketFactory( - SOCKET_OPERATION_TIMEOUT, SOCKET_OPERATION_TIMEOUT, sessionCache), 443)); + SOCKET_OPERATION_TIMEOUT, sessionCache), 443)); ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry); diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 7aee64440ef7..eec19cb8c3b9 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -34,6 +34,7 @@ interface IPowerManager void userActivity(long time, int event, int flags); void wakeUp(long time); void goToSleep(long time, int reason); + void nap(long time); boolean isScreenOn(); void reboot(String reason); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index cc2c002e4e0a..58372f4ebe70 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -426,7 +426,7 @@ public final class PowerManager { * </p> * * @param when The time of the user activity, in the {@link SystemClock#uptimeMillis()} - * time base. This timestamp is used to correctly order the user activity with + * time base. This timestamp is used to correctly order the user activity request with * other power management functions. It should be set * to the timestamp of the input event that caused the user activity. * @param noChangeLights If true, does not cause the keyboard backlight to turn on @@ -457,7 +457,7 @@ public final class PowerManager { * * @param time The time when the request to go to sleep was issued, in the * {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly - * order the user activity with other power management functions. It should be set + * order the go to sleep request with other power management functions. It should be set * to the timestamp of the input event that caused the request to go to sleep. * * @see #userActivity @@ -481,7 +481,7 @@ public final class PowerManager { * * @param time The time when the request to wake up was issued, in the * {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly - * order the user activity with other power management functions. It should be set + * order the wake up request with other power management functions. It should be set * to the timestamp of the input event that caused the request to wake up. * * @see #userActivity @@ -495,6 +495,34 @@ public final class PowerManager { } /** + * Forces the device to start napping. + * <p> + * If the device is currently awake, starts dreaming, otherwise does nothing. + * When the dream ends or if the dream cannot be started, the device will + * either wake up or go to sleep depending on whether there has been recent + * user activity. + * </p><p> + * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. + * </p> + * + * @param time The time when the request to nap was issued, in the + * {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly + * order the nap request with other power management functions. It should be set + * to the timestamp of the input event that caused the request to nap. + * + * @see #wakeUp + * @see #goToSleep + * + * @hide + */ + public void nap(long time) { + try { + mService.nap(time); + } catch (RemoteException e) { + } + } + + /** * Sets the brightness of the backlights (screen, keyboard, button). * <p> * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. diff --git a/core/java/android/os/UserHandle.aidl b/core/java/android/os/UserHandle.aidl new file mode 100644 index 000000000000..4892d322b2d6 --- /dev/null +++ b/core/java/android/os/UserHandle.aidl @@ -0,0 +1,19 @@ +/** + * 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 android.os; + +parcelable UserHandle; diff --git a/core/java/android/service/dreams/Dream.java b/core/java/android/service/dreams/Dream.java index dedfb0cc5561..590acfad680c 100644 --- a/core/java/android/service/dreams/Dream.java +++ b/core/java/android/service/dreams/Dream.java @@ -59,10 +59,10 @@ import com.android.internal.policy.PolicyManager; * <category android:name="android.intent.category.DREAM" /> * </intent-filter> * - * <!-- Point to configuration activity for this dream (optional) --> + * <!-- Point to additional information for this dream (optional) --> * <meta-data - * android:name="android.service.dreams.config_activity" - * android:value="com.example.mypackage/com.example.mypackage.MyDreamSettingsActivity" /> + * android:name="android.service.dream" + * android:resource="@xml/my_dream" /> * </service> * } * </pre> @@ -72,6 +72,12 @@ public class Dream extends Service implements Window.Callback { private final String TAG = Dream.class.getSimpleName() + "[" + getClass().getSimpleName() + "]"; /** + * The name of the dream manager service. + * @hide + */ + public static final String DREAM_SERVICE = "dreams"; + + /** * Used with {@link Intent#ACTION_MAIN} to declare the necessary intent-filter for a dream. * * @see Dream @@ -81,12 +87,12 @@ public class Dream extends Service implements Window.Callback { "android.intent.category.DREAM"; /** - * Service meta-data key for declaring an optional configuration activity. - * - * @see Dream - * */ - public static final String METADATA_NAME_CONFIG_ACTIVITY = - "android.service.dreams.config_activity"; + * Name under which a Dream publishes information about itself. + * This meta-data must reference an XML resource containing + * a <code><{@link android.R.styleable#Dream dream}></code> + * tag. + */ + public static final String DREAM_META_DATA = "android.service.dream"; /** * Broadcast Action: Sent after the system starts dreaming. @@ -361,13 +367,6 @@ public class Dream extends Service implements Window.Callback { return getWindow().findViewById(id); } - /** FIXME remove once platform dreams are updated */ - @Deprecated - protected void lightsOut() { - setLowProfile(true); - setFullscreen(true); - } - /** * Marks this dream as interactive to receive input events. * @@ -506,7 +505,7 @@ public class Dream extends Service implements Window.Callback { // end public api private void loadSandman() { - mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams")); + mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); } private final void attach(IBinder windowToken) { @@ -591,7 +590,7 @@ public class Dream extends Service implements Window.Callback { mFinished = true; if (mSandman != null) { - mSandman.awakenSelf(mWindowToken); + mSandman.finishSelf(mWindowToken); } else { Slog.w(TAG, "No dream manager found"); } diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index bd1c5245ab5e..1c1b39094776 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -30,5 +30,5 @@ interface IDreamManager { ComponentName getDefaultDreamComponent(); void testDream(in ComponentName componentName); boolean isDreaming(); - void awakenSelf(in IBinder token); + void finishSelf(in IBinder token); }
\ No newline at end of file diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index da925c787a2c..28763b3c949d 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -21,6 +21,7 @@ import android.content.ComponentCallbacks2; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.opengl.EGL14; import android.opengl.GLUtils; import android.opengl.ManagedEGLContext; import android.os.Handler; @@ -608,12 +609,6 @@ public abstract class HardwareRenderer { @SuppressWarnings({"deprecation"}) static abstract class GlRenderer extends HardwareRenderer { - // These values are not exposed in our EGL APIs - static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - static final int EGL_OPENGL_ES2_BIT = 4; - static final int EGL_SURFACE_TYPE = 0x3033; - static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400; - static final int SURFACE_STATE_ERROR = 0; static final int SURFACE_STATE_SUCCESS = 1; static final int SURFACE_STATE_UPDATED = 2; @@ -953,19 +948,8 @@ public abstract class HardwareRenderer { return null; } - /* - * Before we can issue GL commands, we need to make sure - * the context is current and bound to a surface. - */ - if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { - throw new Surface.OutOfResourcesException("eglMakeCurrent failed " - + GLUtils.getEGLErrorString(sEgl.eglGetError())); - } - initCaches(); - enableDirtyRegions(); - return mEglContext.getGL(); } @@ -990,7 +974,7 @@ public abstract class HardwareRenderer { abstract void initCaches(); EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { - int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; + int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, mGlVersion != 0 ? attribs : null); @@ -1066,6 +1050,14 @@ public abstract class HardwareRenderer { throw new RuntimeException("createWindowSurface failed " + GLUtils.getEGLErrorString(error)); } + + if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { + throw new IllegalStateException("eglMakeCurrent failed " + + GLUtils.getEGLErrorString(sEgl.eglGetError())); + } + + enableDirtyRegions(); + return true; } @@ -1430,7 +1422,7 @@ public abstract class HardwareRenderer { @Override int[] getConfig(boolean dirtyRegions) { return new int[] { - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, @@ -1439,7 +1431,7 @@ public abstract class HardwareRenderer { // TODO: Find a better way to choose the stencil size EGL_STENCIL_SIZE, mShowOverdraw ? GLES20Canvas.getStencilSize() : 0, EGL_SURFACE_TYPE, EGL_WINDOW_BIT | - (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), + (dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), EGL_NONE }; } diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index 123ce2a92d9b..b0a271198ca2 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -314,7 +314,7 @@ public class ScaleGestureDetector { } } - final boolean configChanged = + final boolean configChanged = action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN; final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; @@ -344,7 +344,7 @@ public class ScaleGestureDetector { if (skipIndex == i) continue; // Average touch major and touch minor and convert the resulting diameter into a radius. - final float touchSize = getAdjustedTouchHistory(event.getPointerId(i)); + final float touchSize = getAdjustedTouchHistory(event.getPointerId(i)) / 2; devSumX += Math.abs(event.getX(i) - focusX) + touchSize; devSumY += Math.abs(event.getY(i) - focusY) + touchSize; } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 3be63d5b5704..0d16dd31b0bc 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -46,13 +46,18 @@ import java.util.concurrent.locks.ReentrantLock; * * <p>The surface is Z ordered so that it is behind the window holding its * SurfaceView; the SurfaceView punches a hole in its window to allow its - * surface to be displayed. The view hierarchy will take care of correctly + * surface to be displayed. The view hierarchy will take care of correctly * compositing with the Surface any siblings of the SurfaceView that would - * normally appear on top of it. This can be used to place overlays such as + * normally appear on top of it. This can be used to place overlays such as * buttons on top of the Surface, though note however that it can have an * impact on performance since a full alpha-blended composite will be performed * each time the Surface changes. * + * <p> The transparent region that makes the surface visible is based on the + * layout positions in the view hierarchy. If the post-layout transform + * properties are used to draw a sibling view on top of the SurfaceView, the + * view may not be properly composited with the surface. + * * <p>Access to the underlying surface is provided via the SurfaceHolder interface, * which can be retrieved by calling {@link #getHolder}. * @@ -62,14 +67,14 @@ import java.util.concurrent.locks.ReentrantLock; * Surface is created and destroyed as the window is shown and hidden. * * <p>One of the purposes of this class is to provide a surface in which a - * secondary thread can render into the screen. If you are going to use it + * secondary thread can render into the screen. If you are going to use it * this way, you need to be aware of some threading semantics: * * <ul> * <li> All SurfaceView and * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called * from the thread running the SurfaceView's window (typically the main thread - * of the application). They thus need to correctly synchronize with any + * of the application). They thus need to correctly synchronize with any * state that is also touched by the drawing thread. * <li> You must ensure that the drawing thread only touches the underlying * Surface while it is valid -- between diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ecacaca62f2d..12eb800fc3a3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2141,6 +2141,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static final int PFLAG2_PADDING_RESOLVED = 0x20000000; + /** + * Flag indicating that the start/end drawables has been resolved into left/right ones. + */ + static final int PFLAG2_DRAWABLE_RESOLVED = 0x40000000; + + /** + * Group of bits indicating that RTL properties resolution is done. + */ + static final int ALL_RTL_PROPERTIES_RESOLVED = PFLAG2_LAYOUT_DIRECTION_RESOLVED | + PFLAG2_TEXT_DIRECTION_RESOLVED | PFLAG2_TEXT_ALIGNMENT_RESOLVED; + // There are a couple of flags left in mPrivateFlags2 /* End of masks for mPrivateFlags2 */ @@ -3199,9 +3210,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mResources = context != null ? context.getResources() : null; mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED; // Set layout and text direction defaults - mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) | + mPrivateFlags2 = + (LAYOUT_DIRECTION_DEFAULT << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) | (TEXT_DIRECTION_DEFAULT << PFLAG2_TEXT_DIRECTION_MASK_SHIFT) | + (PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT) | (TEXT_ALIGNMENT_DEFAULT << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT) | + (PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT) | (IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS); @@ -3419,7 +3433,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, break; case com.android.internal.R.styleable.View_layoutDirection: // Clear any layout direction flags (included resolved bits) already set - mPrivateFlags2 &= ~(PFLAG2_LAYOUT_DIRECTION_MASK | PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK); + mPrivateFlags2 &= + ~(PFLAG2_LAYOUT_DIRECTION_MASK | PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK); // Set the layout direction flags depending on the value of the attribute final int layoutDirection = a.getInt(attr, -1); final int value = (layoutDirection != -1) ? @@ -5772,6 +5787,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #LAYOUT_DIRECTION_INHERIT} or * {@link #LAYOUT_DIRECTION_LOCALE}. * @attr ref android.R.styleable#View_layoutDirection + * + * @hide */ @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "LTR"), @@ -5779,7 +5796,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = LAYOUT_DIRECTION_INHERIT, to = "INHERIT"), @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE, to = "LOCALE") }) - private int getRawLayoutDirection() { + public int getRawLayoutDirection() { return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK) >> PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; } @@ -5787,10 +5804,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Set the layout direction for this view. This will propagate a reset of layout direction * resolution to the view's children and resolve layout direction for this view. * - * @param layoutDirection One of {@link #LAYOUT_DIRECTION_LTR}, - * {@link #LAYOUT_DIRECTION_RTL}, - * {@link #LAYOUT_DIRECTION_INHERIT} or - * {@link #LAYOUT_DIRECTION_LOCALE}. + * @param layoutDirection the layout direction to set. Should be one of: + * + * {@link #LAYOUT_DIRECTION_LTR}, + * {@link #LAYOUT_DIRECTION_RTL}, + * {@link #LAYOUT_DIRECTION_INHERIT}, + * {@link #LAYOUT_DIRECTION_LOCALE}. + * + * Resolution will be done if the value is set to LAYOUT_DIRECTION_INHERIT. The resolution + * proceeds up the parent chain of the view to get the value. If there is no parent, then it + * will return the default {@link #LAYOUT_DIRECTION_LTR}. * * @attr ref android.R.styleable#View_layoutDirection */ @@ -5803,11 +5826,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Set the new layout direction (filtered) mPrivateFlags2 |= ((layoutDirection << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) & PFLAG2_LAYOUT_DIRECTION_MASK); - resolveRtlProperties(); - // Notify changes - onRtlPropertiesChanged(); - // ... and ask for a layout pass - requestLayout(); + // We need to resolve all RTL properties as they all depend on layout direction + resolveRtlPropertiesIfNeeded(); } } @@ -5816,6 +5836,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns * {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL. + * + * For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version + * is lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}. */ @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"), @@ -5827,12 +5850,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED; return LAYOUT_DIRECTION_LTR; } - // The layout direction will be resolved only if needed - if ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED) != PFLAG2_LAYOUT_DIRECTION_RESOLVED) { - resolveLayoutDirection(); - } - return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) == PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? - LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR; + return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) == + PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR; } /** @@ -11474,10 +11493,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, jumpDrawablesToCurrentState(); - resolveRtlProperties(); - // Notify changes - onRtlPropertiesChanged(); - clearAccessibilityFocus(); if (isFocused()) { InputMethodManager imm = InputMethodManager.peekInstance(); @@ -11490,25 +11505,41 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Resolve all RTL related properties + * Resolve all RTL related properties. */ - void resolveRtlProperties() { - // Order is important here: LayoutDirection MUST be resolved first... - resolveLayoutDirection(); + void resolveRtlPropertiesIfNeeded() { + if (!needRtlPropertiesResolution()) return; + + // Order is important here: LayoutDirection MUST be resolved first + if (!isLayoutDirectionResolved()) { + resolveLayoutDirection(); + resolveLayoutParams(); + } // ... then we can resolve the others properties depending on the resolved LayoutDirection. - resolveTextDirection(); - resolveTextAlignment(); - resolvePadding(); - resolveLayoutParams(); - resolveDrawables(); + if (!isTextDirectionResolved()) { + resolveTextDirection(); + } + if (!isTextAlignmentResolved()) { + resolveTextAlignment(); + } + if (!isPaddingResolved()) { + resolvePadding(); + } + if (!isDrawablesResolved()) { + resolveDrawables(); + } + requestLayout(); + invalidate(true); + onRtlPropertiesChanged(); } - // Reset resolution of all RTL related properties + // Reset resolution of all RTL related properties. void resetRtlProperties() { resetResolvedLayoutDirection(); resetResolvedTextDirection(); resetResolvedTextAlignment(); resetResolvedPadding(); + resetResolvedDrawables(); } /** @@ -11538,6 +11569,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @return true if RTL properties need resolution. + */ + private boolean needRtlPropertiesResolution() { + return (mPrivateFlags2 & ALL_RTL_PROPERTIES_RESOLVED) != ALL_RTL_PROPERTIES_RESOLVED; + } + + /** * Called when any RTL property (layout direction or text direction or text alignment) has * been changed. * @@ -11614,7 +11652,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Reset the resolved layout direction. + * Reset the resolved layout direction. Layout direction will be resolved during a call to + * {@link #onMeasure(int, int)}. * * @hide */ @@ -11624,6 +11663,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @return true if the layout direction is inherited. + * * @hide */ public boolean isLayoutDirectionInherited() { @@ -11631,12 +11672,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @return true if layout direction has been resolved. + */ + private boolean isLayoutDirectionResolved() { + return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED) == PFLAG2_LAYOUT_DIRECTION_RESOLVED; + } + + /** * Return if padding has been resolved * * @hide */ boolean isPaddingResolved() { - return (mPrivateFlags2 & PFLAG2_PADDING_RESOLVED) != 0; + return (mPrivateFlags2 & PFLAG2_PADDING_RESOLVED) == PFLAG2_PADDING_RESOLVED; } /** @@ -14116,6 +14164,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mBackground != null) { mBackground.setLayoutDirection(getLayoutDirection()); } + mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED; onResolveDrawables(getLayoutDirection()); } @@ -14134,6 +14183,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void onResolveDrawables(int layoutDirection) { } + private void resetResolvedDrawables() { + mPrivateFlags2 &= ~PFLAG2_DRAWABLE_RESOLVED; + } + + private boolean isDrawablesResolved() { + return (mPrivateFlags2 & PFLAG2_DRAWABLE_RESOLVED) == PFLAG2_DRAWABLE_RESOLVED; + } + /** * If your view subclass is displaying its own Drawable objects, it should * override this function and return true for any Drawable it is @@ -14403,6 +14460,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, padding = new Rect(); sThreadLocal.set(padding); } + resetResolvedDrawables(); background.setLayoutDirection(getLayoutDirection()); if (background.getPadding(padding)) { resetResolvedPadding(); @@ -15379,9 +15437,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // first clears the measured dimension flag mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; - if (!isPaddingResolved()) { - resolvePadding(); - } + resolveRtlPropertiesIfNeeded(); // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -16526,6 +16582,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} + * + * Resolution will be done if the value is set to TEXT_DIRECTION_INHERIT. The resolution + * proceeds up the parent chain of the view to get the value. If there is no parent, then it will + * return the default {@link #TEXT_DIRECTION_FIRST_STRONG}. */ public void setTextDirection(int textDirection) { if (getRawTextDirection() != textDirection) { @@ -16534,6 +16594,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, resetResolvedTextDirection(); // Set the new text direction mPrivateFlags2 |= ((textDirection << PFLAG2_TEXT_DIRECTION_MASK_SHIFT) & PFLAG2_TEXT_DIRECTION_MASK); + // Do resolution + resolveTextDirection(); // Notify change onRtlPropertiesChanged(); // Refresh @@ -16545,11 +16607,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Return the resolved text direction. * - * This needs resolution if the value is TEXT_DIRECTION_INHERIT. The resolution matches what has - * been set by {@link #setTextDirection(int)} if it is not TEXT_DIRECTION_INHERIT, otherwise the - * resolution proceeds up the parent chain of the view. If there is no parent, then it will - * return the default {@link #TEXT_DIRECTION_FIRST_STRONG}. - * * @return the resolved text direction. Returns one of: * * {@link #TEXT_DIRECTION_FIRST_STRONG} @@ -16559,10 +16616,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_DIRECTION_LOCALE} */ public int getTextDirection() { - // The text direction will be resolved only if needed - if ((mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED) != PFLAG2_TEXT_DIRECTION_RESOLVED) { - resolveTextDirection(); - } return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED_MASK) >> PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; } @@ -16601,6 +16654,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { // We cannot do the resolution if there is no parent, so use the default one mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; + // Resolution will need to happen again later + return; } break; case TEXT_DIRECTION_FIRST_STRONG: @@ -16639,16 +16694,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Reset resolved text direction. Text direction can be resolved with a call to - * getTextDirection(). + * Reset resolved text direction. Text direction will be resolved during a call to + * {@link #onMeasure(int, int)}. * * @hide */ public void resetResolvedTextDirection() { + // Reset any previous text direction resolution mPrivateFlags2 &= ~(PFLAG2_TEXT_DIRECTION_RESOLVED | PFLAG2_TEXT_DIRECTION_RESOLVED_MASK); + // Set to default value + mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; } /** + * @return true if text direction is inherited. + * * @hide */ public boolean isTextDirectionInherited() { @@ -16656,6 +16716,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @return true if text direction is resolved. + */ + private boolean isTextDirectionResolved() { + return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED) == PFLAG2_TEXT_DIRECTION_RESOLVED; + } + + /** * Return the value specifying the text alignment or policy that was set with * {@link #setTextAlignment(int)}. * @@ -16697,6 +16764,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} * + * Resolution will be done if the value is set to TEXT_ALIGNMENT_INHERIT. The resolution + * proceeds up the parent chain of the view to get the value. If there is no parent, then it + * will return the default {@link #TEXT_ALIGNMENT_GRAVITY}. + * * @attr ref android.R.styleable#View_textAlignment */ public void setTextAlignment(int textAlignment) { @@ -16705,7 +16776,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags2 &= ~PFLAG2_TEXT_ALIGNMENT_MASK; resetResolvedTextAlignment(); // Set the new text alignment - mPrivateFlags2 |= ((textAlignment << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT) & PFLAG2_TEXT_ALIGNMENT_MASK); + mPrivateFlags2 |= + ((textAlignment << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT) & PFLAG2_TEXT_ALIGNMENT_MASK); + // Do resolution + resolveTextAlignment(); // Notify change onRtlPropertiesChanged(); // Refresh @@ -16717,10 +16791,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Return the resolved text alignment. * - * The resolved text alignment. This needs resolution if the value is - * TEXT_ALIGNMENT_INHERIT. The resolution matches {@link #setTextAlignment(int)} if it is - * not TEXT_ALIGNMENT_INHERIT, otherwise resolution proceeds up the parent chain of the view. - * * @return the resolved text alignment. Returns one of: * * {@link #TEXT_ALIGNMENT_GRAVITY}, @@ -16740,11 +16810,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END") }) public int getTextAlignment() { - // If text alignment is not resolved, then resolve it - if ((mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED) != PFLAG2_TEXT_ALIGNMENT_RESOLVED) { - resolveTextAlignment(); - } - return (mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK) >> PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT; + return (mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK) >> + PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT; } /** @@ -16786,6 +16853,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, else { // We cannot do the resolution if there is no parent so use the default mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; + // Resolution will need to happen again later + return; } break; case TEXT_ALIGNMENT_GRAVITY: @@ -16825,16 +16894,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Reset resolved text alignment. + * Reset resolved text alignment. Text alignment will be resolved during a call to + * {@link #onMeasure(int, int)}. * * @hide */ public void resetResolvedTextAlignment() { // Reset any previous text alignment resolution mPrivateFlags2 &= ~(PFLAG2_TEXT_ALIGNMENT_RESOLVED | PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK); + // Set to default + mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; } /** + * @return true if text alignment is inherited. + * * @hide */ public boolean isTextAlignmentInherited() { @@ -16842,6 +16916,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @return true if text alignment is resolved. + */ + private boolean isTextAlignmentResolved() { + return (mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED) == PFLAG2_TEXT_ALIGNMENT_RESOLVED; + } + + /** * Generate a value suitable for use in {@link #setId(int)}. * This value will not collide with ID values generated at build time by aapt for R.id. * diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 34411ea81de0..41890d6ee3d4 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3391,11 +3391,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (child.hasTransientState()) { childHasTransientStateChanged(child, true); } - - if (child.isLayoutDirectionInherited()) { - child.resetResolvedLayoutDirection(); - child.resolveRtlProperties(); - } } private void addInArray(View child, int index) { @@ -3621,7 +3616,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager childHasTransientStateChanged(view, false); } - view.resetResolvedLayoutDirection(); + view.resetRtlProperties(); onViewRemoved(view); @@ -5261,19 +5256,92 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @hide */ @Override + public void resolveLayoutDirection() { + super.resolveLayoutDirection(); + + int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.isLayoutDirectionInherited()) { + child.resolveLayoutDirection(); + } + } + } + + /** + * @hide + */ + @Override + public void resolveTextDirection() { + super.resolveTextDirection(); + + int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.isTextDirectionInherited()) { + child.resolveTextDirection(); + } + } + } + + /** + * @hide + */ + @Override + public void resolveTextAlignment() { + super.resolveTextAlignment(); + + int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.isTextAlignmentInherited()) { + child.resolveTextAlignment(); + } + } + } + + /** + * @hide + */ + @Override public void resetResolvedLayoutDirection() { super.resetResolvedLayoutDirection(); - // Take care of resetting the children resolution too int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.isLayoutDirectionInherited()) { child.resetResolvedLayoutDirection(); } + } + } + + /** + * @hide + */ + @Override + public void resetResolvedTextDirection() { + super.resetResolvedTextDirection(); + + int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); if (child.isTextDirectionInherited()) { child.resetResolvedTextDirection(); } + } + } + + /** + * @hide + */ + @Override + public void resetResolvedTextAlignment() { + super.resetResolvedTextAlignment(); + + int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); if (child.isTextAlignmentInherited()) { child.resetResolvedTextAlignment(); } diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index 63a0870307f7..c7da818907d4 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -36,6 +36,10 @@ import java.util.TimeZone; /** * This widget display an analogic clock with two hands for hours and * minutes. + * + * @attr ref android.R.styleable#AnalogClock_dial + * @attr ref android.R.styleable#AnalogClock_hand_hour + * @attr ref android.R.styleable#AnalogClock_hand_minute */ @RemoteView public class AnalogClock extends View { diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 87396fbc13d0..7ca83223bee3 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -789,6 +789,7 @@ public class ImageView extends View { if (resizeWidth) { int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; + widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); if (newWidth <= widthSize) { widthSize = newWidth; done = true; @@ -799,6 +800,7 @@ public class ImageView extends View { if (!done && resizeHeight) { int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; + heightSize = resolveAdjustedSize(newHeight, mMaxHeight, heightMeasureSpec); if (newHeight <= heightSize) { heightSize = newHeight; } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 90f55bf78797..4b5dfb84d742 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -2166,6 +2166,8 @@ public class RemoteViews implements Parcelable, Filter { * @param value The value to pass to the method. */ public void setUri(int viewId, String methodName, Uri value) { + // Resolve any filesystem path before sending remotely + value = value.getCanonicalUri(); addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value)); } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index bffbe119724c..4ad0819467ed 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -436,9 +436,10 @@ public class ResolverActivity extends AlertActivity implements AdapterView.OnIte if (mBaseResolveList != null) { mCurrentResolveList = mBaseResolveList; } else { - mCurrentResolveList = mPm.queryIntentActivities( + mCurrentResolveList = mPm.queryIntentActivitiesAsUser( mIntent, PackageManager.MATCH_DEFAULT_ONLY - | (mAlwaysUseOption ? PackageManager.GET_RESOLVED_FILTER : 0)); + | (mAlwaysUseOption ? PackageManager.GET_RESOLVED_FILTER : 0), + UserHandle.getUserId(mLaunchedFromUid)); // Filter out any activities that the launched uid does not // have permission for. We don't do this when we have an explicit // list of resolved activities, because that only happens when diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 0755038e8ffc..9759bdcb0f7c 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5305,6 +5305,19 @@ <attr name="description" /> </declare-styleable> + <!-- Use <code>dream</code> as the root tag of the XML resource that + describes an + {@link android.service.dreams.Dream}, which is + referenced from its + {@link android.service.dreams.Dream#DREAM_META_DATA} + meta-data entry. Described here are the attributes that can be + included in that tag. --> + <declare-styleable name="Dream"> + <!-- Component name of an activity that allows the user to modify + the settings for this dream. --> + <attr name="settingsActivity" /> + </declare-styleable> + <!-- =============================== --> <!-- Accounts package class attributes --> <!-- =============================== --> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java index 39e2cf218447..79d928c64b1b 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java @@ -219,15 +219,16 @@ public class WifiStressTest // Stress Wifi reconnection to secure net after sleep @LargeTest public void testWifiReconnectionAfterSleep() { - int value = Settings.System.getInt(mRunner.getContext().getContentResolver(), - Settings.System.WIFI_SLEEP_POLICY, -1); - if (value < 0) { - Settings.System.putInt(mRunner.getContext().getContentResolver(), - Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT); + int value = Settings.Global.getInt(mRunner.getContext().getContentResolver(), + Settings.Global.WIFI_SLEEP_POLICY, -1); + log("wifi sleep policy is: " + value); + if (value != Settings.Global.WIFI_SLEEP_POLICY_DEFAULT) { + Settings.Global.putInt(mRunner.getContext().getContentResolver(), + Settings.Global.WIFI_SLEEP_POLICY, Settings.Global.WIFI_SLEEP_POLICY_DEFAULT); log("set wifi sleep policy to default value"); } - Settings.Secure.putLong(mRunner.getContext().getContentResolver(), - Settings.Secure.WIFI_IDLE_MS, WIFI_IDLE_MS); + Settings.Global.putLong(mRunner.getContext().getContentResolver(), + Settings.Global.WIFI_IDLE_MS, WIFI_IDLE_MS); // Connect to a Wi-Fi network WifiConfiguration config = new WifiConfiguration(); diff --git a/docs/html/about/versions/jelly-bean.jd b/docs/html/about/versions/jelly-bean.jd index 485a1bbce407..0583e1215643 100644 --- a/docs/html/about/versions/jelly-bean.jd +++ b/docs/html/about/versions/jelly-bean.jd @@ -303,12 +303,12 @@ style="font-weight:500;">App Widgets</span> can resize automatically to fit the <p>Smart app updates is a new feature of Google Play that introduces a better way of delivering <strong>app updates</strong> to devices. When developers publish an update, Google Play now delivers only the <strong>bits that have changed</strong> to devices, rather than the entire APK. This makes the updates much lighter-weight in most cases, so they are faster to download, save the device’s battery, and conserve bandwidth usage on users’ mobile data plan. On average, a smart app update is about <strong>1/3 the size</strong> of a full APK update.</p> -<h3 id="gps">Google Play services (coming soon)</h3> +<h3 id="gps">Google Play services</h3> <p>Google Play services helps developers to <strong>integrate Google services</strong> such as authentication and Google+ into their apps delivered through Google Play.</p> -<p>Google Play services will be automatically provisioned to end user devices by Google Play, so all you need is a <strong>thin client library</strong> in your apps.</p> +<p>Google Play services is automatically provisioned to end user devices by Google Play, so all you need is a <strong>thin client library</strong> in your apps.</p> <p>Because your app only contains the small client library, you can take advantage of these services without a big increase in download size and storage footprint. Also, Google Play will <strong>deliver regular updates</strong> to the services, without developers needing to publish app updates to take advantage of them.</p> -<p>For more information about the APIs included in Google Play Services, see the <a href="http://developers.google.com/android/google-play-services/index.html">Google Play Services</a> developer page.</p> +<p>For more information about the APIs included in Google Play Services, see the <a href="http://developers.google.com/android/google-play-services/index.html">Google Play services</a> developer page.</p> diff --git a/docs/html/guide/google/play/services.jd b/docs/html/guide/google/play/services.jd index 519d78b28efd..092642c26d71 100644 --- a/docs/html/guide/google/play/services.jd +++ b/docs/html/guide/google/play/services.jd @@ -1,24 +1,25 @@ page.title=Google Play Services - @jd:body - -<p>Google Play services is a platform that is delivered through the Google Play Store that +<p> + Google Play services is a platform that is delivered through the Google Play Store that offers integration with Google products, such as Google+, into Android apps. The Google Play services framework consists of a services component - that runs on the device and a thin client library that you package with your app. </p> - - + that runs on the device and a thin client library that you package with your app. +</p> <div class="distribute-features col-13"> - <ul> - <li style="border-top: 1px solid #F80;"><h5>Easy Authentication</h5> Your app can leverage the user's - existing Google account on the device without having to go through - tedious authentication flows. A few clicks from the user and you're set! - <br /> <a href="https://developers.google.com/android/google-play-services">Learn more »</a> +<ul> + <li style="border-top: 1px solid #F80;"><h5>Easy Authentication</h5> + Your app can leverage the user's existing Google account on the device without having to go + through tedious authentication flows. A few clicks from the user and you're set! + <br/> + <a href="https://developers.google.com/android/google-play-services">Learn more »</a> </li> - <li style="border-top: 1px solid #F80;"><h5>Google+ Integration</h5> Google Play services makes it - easy for your app to integrate with Sign in with Google+, +1 button, and Google+ history. - <br /> <a href="https://developers.google.com/android/google-play-services">Learn more »</a> - </li> - </ul> - -</div>
\ No newline at end of file + <li style="border-top: 1px solid #F80;"><h5>Google+ Integration</h5> + Google Play services makes it easy for your app to integrate with Google+ sign-in and the +1 + button. + <br/> + <a href="https://developers.google.com/android/google-play-services">Learn more »</a> + </li> +</ul> + +</div> diff --git a/docs/html/guide/topics/appwidgets/index.jd b/docs/html/guide/topics/appwidgets/index.jd index 5a4e03ac8db3..7e031d9daaac 100644 --- a/docs/html/guide/topics/appwidgets/index.jd +++ b/docs/html/guide/topics/appwidgets/index.jd @@ -117,7 +117,7 @@ to modify App Widget settings at create-time.</p> application's <code>AndroidManifest.xml</code> file. For example:</p> -<pre> +<pre style="clear:right"> <receiver android:name="ExampleAppWidgetProvider" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> @@ -815,8 +815,7 @@ href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget sample</a>:</p> <p> -<img src="{@docRoot}resources/samples/images/StackWidget.png" alt="StackView -Widget" /> +<img src="{@docRoot}resources/images/StackWidget.png" alt="" /> </p> <p>This sample consists of a stack of 10 views, which display the values diff --git a/graphics/java/android/renderscript/ScriptIntrinsicBlend.java b/graphics/java/android/renderscript/ScriptIntrinsicBlend.java index 13c03af7dbe6..65c69c01aafb 100644 --- a/graphics/java/android/renderscript/ScriptIntrinsicBlend.java +++ b/graphics/java/android/renderscript/ScriptIntrinsicBlend.java @@ -36,17 +36,18 @@ public class ScriptIntrinsicBlend extends ScriptIntrinsic { * @return ScriptIntrinsicBlend */ public static ScriptIntrinsicBlend create(RenderScript rs, Element e) { - int id = rs.nScriptIntrinsicCreate(6, e.getID(rs)); + // 7 comes from RS_SCRIPT_INTRINSIC_ID_BLEND in rsDefines.h + int id = rs.nScriptIntrinsicCreate(7, e.getID(rs)); return new ScriptIntrinsicBlend(id, rs); } private void blend(int id, Allocation ain, Allocation aout) { - if (ain.getElement() != Element.U8_4(mRS)) { - throw new RSIllegalArgumentException("Input not of expected format."); + if (!ain.getElement().isCompatible(Element.U8_4(mRS))) { + throw new RSIllegalArgumentException("Input is not of expected format."); } - if (aout.getElement() != Element.U8_4(mRS)) { - throw new RSIllegalArgumentException("Output not of expected format."); + if (!aout.getElement().isCompatible(Element.U8_4(mRS))) { + throw new RSIllegalArgumentException("Output is not of expected format."); } forEach(id, ain, aout, null); } diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index fb525ee428ac..882e4bbc4bb1 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -19,6 +19,7 @@ #include <utils/Log.h> #include "Layer.h" +#include "LayerRenderer.h" #include "OpenGLRenderer.h" #include "Caches.h" @@ -46,14 +47,15 @@ Layer::~Layer() { if (mesh) delete mesh; if (meshIndices) delete meshIndices; if (colorFilter) Caches::getInstance().resourceCache.decrementRefcount(colorFilter); - if (fbo) Caches::getInstance().fboCache.put(fbo); + removeFbo(); deleteTexture(); } -void Layer::freeResourcesLocked() { - if (colorFilter) { - Caches::getInstance().resourceCache.decrementRefcountLocked(colorFilter); - colorFilter = NULL; +void Layer::removeFbo() { + if (fbo) { + LayerRenderer::flushLayer(this); + Caches::getInstance().fboCache.put(fbo); + fbo = 0; } } diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 9b6205d31f28..69be3178ff9d 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -48,7 +48,7 @@ struct Layer { Layer(const uint32_t layerWidth, const uint32_t layerHeight); ~Layer(); - void freeResourcesLocked(); + void removeFbo(); /** * Sets this layer's region to a rectangle. Computes the appropriate diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 799aea3755a5..f2e7f66ac503 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -299,13 +299,6 @@ void LayerRenderer::destroyLayer(Layer* layer) { LAYER_RENDERER_LOGD("Recycling layer, %dx%d fbo = %d", layer->getWidth(), layer->getHeight(), layer->getFbo()); - GLuint fbo = layer->getFbo(); - if (fbo) { - flushLayer(layer); - Caches::getInstance().fboCache.put(fbo); - layer->setFbo(0); - } - if (!Caches::getInstance().layerCache.put(layer)) { LAYER_RENDERER_LOGD(" Destroyed!"); Caches::getInstance().resourceCache.decrementRefcount(layer); @@ -314,6 +307,7 @@ void LayerRenderer::destroyLayer(Layer* layer) { #if DEBUG_LAYER_RENDERER Caches::getInstance().layerCache.dump(); #endif + layer->removeFbo(); layer->region.clear(); } } diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index 392f8634c08c..acedbcc547c8 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -60,6 +60,8 @@ public: ANDROID_API static void destroyLayerDeferred(Layer* layer); ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap); + static void flushLayer(Layer* layer); + protected: virtual bool hasLayer(); virtual Region* getRegion(); @@ -69,8 +71,6 @@ protected: private: void generateMesh(); - static void flushLayer(Layer* layer); - Layer* mLayer; }; // class LayerRenderer diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index d0d1d93489df..87c3a4723b79 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2729,13 +2729,13 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* pain mCaches.activeTexture(0); if (CC_LIKELY(!layer->region.isEmpty())) { + SkiaColorFilter* oldFilter = mColorFilter; + mColorFilter = layer->getColorFilter(); + if (layer->region.isRect()) { composeLayerRect(layer, layer->regionRect); } else if (layer->mesh) { const float a = layer->getAlpha() / 255.0f; - SkiaColorFilter *oldFilter = mColorFilter; - mColorFilter = layer->getColorFilter(); - setupDraw(); setupDrawWithTexture(); setupDrawColor(a, a, a, a); @@ -2764,13 +2764,13 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* pain finishDrawTexture(); - mColorFilter = oldFilter; - #if DEBUG_LAYERS_AS_REGIONS drawRegionRects(layer->region); #endif } + mColorFilter = oldFilter; + if (debugLayerUpdate) { drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), 0x7f00ff00, SkXfermode::kSrcOver_Mode); diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index 5b1b57d3a664..f0b5553a79a7 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -81,6 +81,7 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons if (mInitialized) { transform = addUniform("transform"); + projection = addUniform("projection"); } } @@ -152,18 +153,20 @@ GLuint Program::buildShader(const char* source, GLenum type) { void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, const mat4& transformMatrix, bool offset) { - mat4 t(projectionMatrix); + mat4 p(projectionMatrix); if (offset) { // offset screenspace xy by an amount that compensates for typical precision // issues in GPU hardware that tends to paint hor/vert lines in pixels shifted // up and to the left. // This offset value is based on an assumption that some hardware may use as // little as 12.4 precision, so we offset by slightly more than 1/16. - t.translate(.375, .375, 0); + p.translate(.375, .375, 0); } - t.multiply(transformMatrix); + + mat4 t(transformMatrix); t.multiply(modelViewMatrix); + glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]); glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]); } diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index b1cb44690265..7e3aacf90260 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -374,6 +374,11 @@ public: */ int transform; + /** + * Name of the projection uniform. + */ + int projection; + protected: /** * Adds an attribute with the specified name. diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 7bc2b376b43b..f536adeff374 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -48,6 +48,7 @@ const char* gVS_Header_Attributes_AAVertexShapeParameters = const char* gVS_Header_Uniforms_TextureTransform = "uniform mat4 mainTextureTransform;\n"; const char* gVS_Header_Uniforms = + "uniform mat4 projection;\n" \ "uniform mat4 transform;\n"; const char* gVS_Header_Uniforms_IsPoint = "uniform mediump float pointSize;\n"; @@ -104,28 +105,28 @@ const char* gVS_Main_OutTransformedTexCoords = const char* gVS_Main_OutGradient[6] = { // Linear " linear = vec2((screenSpace * position).x, 0.5);\n" - " ditherTexCoords = (gl_Position * ditherSize).xy;\n", + " ditherTexCoords = (transform * position).xy * ditherSize;\n", " linear = (screenSpace * position).x;\n" - " ditherTexCoords = (gl_Position * ditherSize).xy;\n", + " ditherTexCoords = (transform * position).xy * ditherSize;\n", // Circular " circular = (screenSpace * position).xy;\n" - " ditherTexCoords = (gl_Position * ditherSize).xy;\n", + " ditherTexCoords = (transform * position).xy * ditherSize;\n", " circular = (screenSpace * position).xy;\n" - " ditherTexCoords = (gl_Position * ditherSize).xy;\n", + " ditherTexCoords = (transform * position).xy * ditherSize;\n", // Sweep " sweep = (screenSpace * position).xy;\n" - " ditherTexCoords = (gl_Position * ditherSize).xy;\n", + " ditherTexCoords = (transform * position).xy * ditherSize;\n", " sweep = (screenSpace * position).xy;\n" - " ditherTexCoords = (gl_Position * ditherSize).xy;\n", + " ditherTexCoords = (transform * position).xy * ditherSize;\n", }; const char* gVS_Main_OutBitmapTexCoords = " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; const char* gVS_Main_OutPointBitmapTexCoords = " outPointBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; const char* gVS_Main_Position = - " gl_Position = transform * position;\n"; + " gl_Position = projection * transform * position;\n"; const char* gVS_Main_PointSize = " gl_PointSize = pointSize;\n"; const char* gVS_Main_AALine = diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index 1adf2c77e7c3..03e2172a7aaf 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -38,6 +38,8 @@ struct Texture { firstFilter = true; firstWrap = true; + + id = 0; } void setWrap(GLenum wrap, bool bindTexture = false, bool force = false, diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl index 44a0333ed76d..0872f1dcc86c 100644 --- a/media/java/android/media/IRingtonePlayer.aidl +++ b/media/java/android/media/IRingtonePlayer.aidl @@ -17,6 +17,7 @@ package android.media; import android.net.Uri; +import android.os.UserHandle; /** * @hide @@ -28,6 +29,6 @@ interface IRingtonePlayer { boolean isPlaying(IBinder token); /** Used for Notification sound playback. */ - void playAsync(in Uri uri, boolean looping, int streamType); + void playAsync(in Uri uri, in UserHandle user, boolean looping, int streamType); void stopAsync(); } diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index 23f7b5566a75..f190eb978423 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -225,8 +225,9 @@ public class Ringtone { mLocalPlayer.start(); } } else if (mAllowRemote) { + final Uri canonicalUri = mUri.getCanonicalUri(); try { - mRemotePlayer.play(mRemoteToken, mUri, mStreamType); + mRemotePlayer.play(mRemoteToken, canonicalUri, mStreamType); } catch (RemoteException e) { Log.w(TAG, "Problem playing ringtone: " + e); } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 4d241ed92c9a..a7294ec86589 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -5,6 +5,7 @@ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.ACCESS_ALL_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.INJECT_EVENTS" /> diff --git a/packages/SystemUI/src/com/android/systemui/Somnambulator.java b/packages/SystemUI/src/com/android/systemui/Somnambulator.java index 89d4ef7444e3..bd872381442c 100644 --- a/packages/SystemUI/src/com/android/systemui/Somnambulator.java +++ b/packages/SystemUI/src/com/android/systemui/Somnambulator.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.content.Intent; import android.os.RemoteException; import android.os.ServiceManager; +import android.service.dreams.Dream; import android.service.dreams.IDreamManager; import android.util.Slog; @@ -45,7 +46,7 @@ public class Somnambulator extends Activity { setResult(RESULT_OK, resultIntent); } else { IDreamManager somnambulist = IDreamManager.Stub.asInterface( - ServiceManager.checkService("dreams")); + ServiceManager.checkService(Dream.DREAM_SERVICE)); if (somnambulist != null) { try { Slog.v("Somnambulator", "Dreaming by user request."); diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java index 3502b62b9df8..0c6e59c7c4e9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java @@ -17,6 +17,7 @@ package com.android.systemui.media; import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; import android.media.IAudioService; import android.media.IRingtonePlayer; import android.media.Ringtone; @@ -26,6 +27,7 @@ import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.util.Slog; import com.android.systemui.SystemUI; @@ -70,9 +72,10 @@ public class RingtonePlayer extends SystemUI { private final IBinder mToken; private final Ringtone mRingtone; - public Client(IBinder token, Uri uri, int streamType) { + public Client(IBinder token, Uri uri, UserHandle user, int streamType) { mToken = token; - mRingtone = new Ringtone(mContext, false); + + mRingtone = new Ringtone(getContextForUser(user), false); mRingtone.setStreamType(streamType); mRingtone.setUri(uri); } @@ -90,12 +93,16 @@ public class RingtonePlayer extends SystemUI { private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() { @Override public void play(IBinder token, Uri uri, int streamType) throws RemoteException { - if (LOGD) Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ")"); + if (LOGD) { + Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid=" + + Binder.getCallingUid() + ")"); + } Client client; synchronized (mClients) { client = mClients.get(token); if (client == null) { - client = new Client(token, uri, streamType); + final UserHandle user = Binder.getCallingUserHandle(); + client = new Client(token, uri, user, streamType); token.linkToDeath(client, 0); mClients.put(token, client); } @@ -131,12 +138,13 @@ public class RingtonePlayer extends SystemUI { } @Override - public void playAsync(Uri uri, boolean looping, int streamType) { - if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ")"); + public void playAsync(Uri uri, UserHandle user, boolean looping, int streamType) { + if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")"); if (Binder.getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("Async playback only available from system UID."); } - mAsyncPlayer.play(mContext, uri, looping, streamType); + + mAsyncPlayer.play(getContextForUser(user), uri, looping, streamType); } @Override @@ -149,6 +157,14 @@ public class RingtonePlayer extends SystemUI { } }; + private Context getContextForUser(UserHandle user) { + try { + return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); + } catch (NameNotFoundException e) { + throw new RuntimeException(e); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Clients:"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index e6aa632eeb1a..d72632faa874 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -45,6 +45,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.service.dreams.Dream; import android.service.dreams.IDreamManager; import android.util.DisplayMetrics; import android.util.Log; @@ -262,7 +263,7 @@ public class PhoneStatusBar extends BaseStatusBar { .getDefaultDisplay(); mDreamManager = IDreamManager.Stub.asInterface( - ServiceManager.checkService("dreams")); + ServiceManager.checkService(Dream.DREAM_SERVICE)); super.start(); // calls createAndAddWindows() diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index 8ad5a919cdd9..4a8bf720aae1 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -16,9 +16,6 @@ package com.android.server; -import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK; -import static android.provider.Settings.Secure.SCREENSAVER_ENABLED; - import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -27,16 +24,12 @@ import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Handler; -import android.os.Looper; import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.PowerManager; import android.os.SystemClock; import android.os.UEventObserver; import android.os.UserHandle; import android.provider.Settings; -import android.service.dreams.IDreamManager; import android.util.Log; import android.util.Slog; @@ -48,14 +41,10 @@ import java.io.FileReader; */ final class DockObserver extends UEventObserver { private static final String TAG = DockObserver.class.getSimpleName(); - private static final boolean LOG = false; private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock"; private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state"; - private static final int DEFAULT_SCREENSAVER_ENABLED = 1; - private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1; - private static final int MSG_DOCK_STATE_CHANGED = 0; private final Object mLock = new Object(); @@ -66,11 +55,16 @@ final class DockObserver extends UEventObserver { private boolean mSystemReady; private final Context mContext; + private final PowerManager mPowerManager; + private final PowerManager.WakeLock mWakeLock; public DockObserver(Context context) { mContext = context; - init(); // set initial status + mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + + init(); // set initial status startObserving(DOCK_UEVENT_MATCH); } @@ -87,17 +81,9 @@ final class DockObserver extends UEventObserver { mPreviousDockState = mDockState; mDockState = newState; if (mSystemReady) { - // Don't force screen on when undocking from the desk dock. - // The change in power state will do this anyway. - // FIXME - we should be configurable. - if ((mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK - && mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK - && mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) || - mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { - PowerManager pm = - (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); - pm.wakeUp(SystemClock.uptimeMillis()); - } + // Wake up immediately when docked or undocked. + mPowerManager.wakeUp(SystemClock.uptimeMillis()); + updateLocked(); } } @@ -138,6 +124,7 @@ final class DockObserver extends UEventObserver { } private void updateLocked() { + mWakeLock.acquire(); mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED); } @@ -145,8 +132,8 @@ final class DockObserver extends UEventObserver { synchronized (mLock) { Slog.i(TAG, "Dock state changed: " + mDockState); + // Skip the dock intent if not yet provisioned. final ContentResolver cr = mContext.getContentResolver(); - if (Settings.Global.getInt(cr, Settings.Global.DEVICE_PROVISIONED, 0) == 0) { Slog.i(TAG, "Device not provisioned, skipping dock broadcast"); @@ -158,16 +145,8 @@ final class DockObserver extends UEventObserver { intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); - // Check if this is Bluetooth Dock - // TODO(BT): Get Dock address. - // String address = null; - // if (address != null) { - // intent.putExtra(BluetoothDevice.EXTRA_DEVICE, - // BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address)); - // } - - // User feedback to confirm dock connection. Particularly - // useful for flaky contact pins... + // Play a sound to provide feedback to confirm dock connection. + // Particularly useful for flaky contact pins... if (Settings.Global.getInt(cr, Settings.Global.DOCK_SOUNDS_ENABLED, 1) == 1) { String whichSound = null; @@ -204,42 +183,14 @@ final class DockObserver extends UEventObserver { } } - IDreamManager mgr = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams")); - if (mgr != null) { - // dreams feature enabled - boolean undocked = mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED; - if (undocked) { - try { - if (mgr.isDreaming()) { - mgr.awaken(); - } - } catch (RemoteException e) { - Slog.w(TAG, "Unable to awaken!", e); - } - } else { - if (isScreenSaverEnabled(mContext) && isScreenSaverActivatedOnDock(mContext)) { - try { - mgr.dream(); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to dream!", e); - } - } - } - } else { - // dreams feature not enabled, send legacy intent - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - } - } + // Send the dock event intent. + // There are many components in the system watching for this so as to + // adjust audio routing, screen orientation, etc. + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - private static boolean isScreenSaverEnabled(Context context) { - return Settings.Secure.getInt(context.getContentResolver(), - SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED) != 0; - } - - private static boolean isScreenSaverActivatedOnDock(Context context) { - return Settings.Secure.getInt(context.getContentResolver(), - SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK) != 0; + // Release the wake lock that was acquired when the message was posted. + mWakeLock.release(); + } } private final Handler mHandler = new Handler(true /*async*/) { diff --git a/services/java/com/android/server/DreamController.java b/services/java/com/android/server/DreamController.java deleted file mode 100644 index 498e581b1854..000000000000 --- a/services/java/com/android/server/DreamController.java +++ /dev/null @@ -1,217 +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; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.IBinder.DeathRecipient; -import android.service.dreams.IDreamService; -import android.util.Slog; -import android.view.IWindowManager; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; - -import com.android.internal.util.DumpUtils; - -import java.io.PrintWriter; -import java.util.NoSuchElementException; - -/** - * Internal controller for starting and stopping the current dream and managing related state. - * - * Assumes all operations (except {@link #dump}) are called from a single thread. - */ -final class DreamController { - private static final boolean DEBUG = true; - private static final String TAG = DreamController.class.getSimpleName(); - - public interface Listener { - void onDreamStopped(boolean wasTest); - } - - private final Context mContext; - private final IWindowManager mIWindowManager; - private final DeathRecipient mDeathRecipient; - private final ServiceConnection mServiceConnection; - private final Listener mListener; - - private Handler mHandler; - - private ComponentName mCurrentDreamComponent; - private IDreamService mCurrentDream; - private Binder mCurrentDreamToken; - private boolean mCurrentDreamIsTest; - - public DreamController(Context context, DeathRecipient deathRecipient, - ServiceConnection serviceConnection, Listener listener) { - mContext = context; - mDeathRecipient = deathRecipient; - mServiceConnection = serviceConnection; - mListener = listener; - mIWindowManager = WindowManagerGlobal.getWindowManagerService(); - } - - public void setHandler(Handler handler) { - mHandler = handler; - } - - public void dump(PrintWriter pw) { - if (mHandler== null || pw == null) { - return; - } - DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() { - @Override - public void dump(PrintWriter pw) { - pw.print(" component="); pw.println(mCurrentDreamComponent); - pw.print(" token="); pw.println(mCurrentDreamToken); - pw.print(" dream="); pw.println(mCurrentDream); - } - }, pw, 200); - } - - public void start(ComponentName dream, boolean isTest) { - if (DEBUG) Slog.v(TAG, String.format("start(%s,%s)", dream, isTest)); - - if (mCurrentDreamComponent != null ) { - if (dream.equals(mCurrentDreamComponent) && isTest == mCurrentDreamIsTest) { - if (DEBUG) Slog.v(TAG, "Dream is already started: " + dream); - return; - } - // stop the current dream before starting a new one - stop(); - } - - mCurrentDreamComponent = dream; - mCurrentDreamIsTest = isTest; - mCurrentDreamToken = new Binder(); - - try { - if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurrentDreamToken - + " for window type: " + WindowManager.LayoutParams.TYPE_DREAM); - mIWindowManager.addWindowToken(mCurrentDreamToken, - WindowManager.LayoutParams.TYPE_DREAM); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to add window token."); - stop(); - return; - } - - Intent intent = new Intent(Intent.ACTION_MAIN) - .setComponent(mCurrentDreamComponent) - .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - .putExtra("android.dreams.TEST", mCurrentDreamIsTest); - - if (!mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)) { - Slog.w(TAG, "Unable to bind service"); - stop(); - return; - } - if (DEBUG) Slog.v(TAG, "Bound service"); - } - - public void attach(ComponentName name, IBinder dream) { - if (DEBUG) Slog.v(TAG, String.format("attach(%s,%s)", name, dream)); - mCurrentDream = IDreamService.Stub.asInterface(dream); - - boolean linked = linkDeathRecipient(dream); - if (!linked) { - stop(); - return; - } - - try { - if (DEBUG) Slog.v(TAG, "Attaching with token:" + mCurrentDreamToken); - mCurrentDream.attach(mCurrentDreamToken); - } catch (Throwable ex) { - Slog.w(TAG, "Unable to send window token to dream:" + ex); - stop(); - } - } - - public void stop() { - if (DEBUG) Slog.v(TAG, "stop()"); - - if (mCurrentDream != null) { - unlinkDeathRecipient(mCurrentDream.asBinder()); - - if (DEBUG) Slog.v(TAG, "Unbinding: " + mCurrentDreamComponent + " service: " + mCurrentDream); - mContext.unbindService(mServiceConnection); - } - if (mCurrentDreamToken != null) { - removeWindowToken(mCurrentDreamToken); - } - - final boolean wasTest = mCurrentDreamIsTest; - mCurrentDream = null; - mCurrentDreamToken = null; - mCurrentDreamComponent = null; - mCurrentDreamIsTest = false; - - if (mListener != null && mHandler != null) { - mHandler.post(new Runnable(){ - @Override - public void run() { - mListener.onDreamStopped(wasTest); - }}); - } - } - - public void stopSelf(IBinder token) { - if (DEBUG) Slog.v(TAG, String.format("stopSelf(%s)", token)); - if (token == null || token != mCurrentDreamToken) { - Slog.w(TAG, "Stop requested for non-current dream token: " + token); - } else { - stop(); - } - } - - private void removeWindowToken(IBinder token) { - if (DEBUG) Slog.v(TAG, "Removing window token: " + token); - try { - mIWindowManager.removeWindowToken(token); - } catch (Throwable e) { - Slog.w(TAG, "Error removing window token", e); - } - } - - private boolean linkDeathRecipient(IBinder dream) { - if (DEBUG) Slog.v(TAG, "Linking death recipient"); - try { - dream.linkToDeath(mDeathRecipient, 0); - return true; - } catch (RemoteException e) { - Slog.w(TAG, "Unable to link death recipient", e); - return false; - } - } - - private void unlinkDeathRecipient(IBinder dream) { - if (DEBUG) Slog.v(TAG, "Unlinking death recipient"); - try { - dream.unlinkToDeath(mDeathRecipient, 0); - } catch (NoSuchElementException e) { - // we tried - } - } - -}
\ No newline at end of file diff --git a/services/java/com/android/server/DreamManagerService.java b/services/java/com/android/server/DreamManagerService.java deleted file mode 100644 index b02ea7f768fd..000000000000 --- a/services/java/com/android/server/DreamManagerService.java +++ /dev/null @@ -1,387 +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; - -import static android.provider.Settings.Secure.SCREENSAVER_COMPONENTS; -import static android.provider.Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT; - -import android.app.ActivityManagerNative; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.RemoteException; -import android.os.UserHandle; -import android.provider.Settings; -import android.service.dreams.Dream; -import android.service.dreams.IDreamManager; -import android.util.Slog; -import android.util.SparseArray; - -import java.io.FileDescriptor; -import java.io.PrintWriter; - -/** - * Service api for managing dreams. - * - * @hide - */ -public final class DreamManagerService - extends IDreamManager.Stub - implements ServiceConnection { - private static final boolean DEBUG = true; - private static final String TAG = DreamManagerService.class.getSimpleName(); - - private static final Intent mDreamingStartedIntent = new Intent(Dream.ACTION_DREAMING_STARTED) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - private static final Intent mDreamingStoppedIntent = new Intent(Dream.ACTION_DREAMING_STOPPED) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - - private final Object mLock = new Object(); - private final DreamController mController; - private final DreamControllerHandler mHandler; - private final Context mContext; - - private final CurrentUserManager mCurrentUserManager = new CurrentUserManager(); - - private final DeathRecipient mAwakenOnBinderDeath = new DeathRecipient() { - @Override - public void binderDied() { - if (DEBUG) Slog.v(TAG, "binderDied()"); - awaken(); - } - }; - - private final DreamController.Listener mControllerListener = new DreamController.Listener() { - @Override - public void onDreamStopped(boolean wasTest) { - synchronized(mLock) { - setDreamingLocked(false, wasTest); - } - }}; - - private boolean mIsDreaming; - - public DreamManagerService(Context context) { - if (DEBUG) Slog.v(TAG, "DreamManagerService startup"); - mContext = context; - mController = new DreamController(context, mAwakenOnBinderDeath, this, mControllerListener); - mHandler = new DreamControllerHandler(mController); - mController.setHandler(mHandler); - } - - public void systemReady() { - mCurrentUserManager.init(mContext); - - if (DEBUG) Slog.v(TAG, "Ready to dream!"); - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - pw.println("Dreamland:"); - mController.dump(pw); - mCurrentUserManager.dump(pw); - } - - // begin IDreamManager api - @Override - public ComponentName[] getDreamComponents() { - checkPermission(android.Manifest.permission.READ_DREAM_STATE); - int userId = UserHandle.getCallingUserId(); - - final long ident = Binder.clearCallingIdentity(); - try { - return getDreamComponentsForUser(userId); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public void setDreamComponents(ComponentName[] componentNames) { - checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); - int userId = UserHandle.getCallingUserId(); - - final long ident = Binder.clearCallingIdentity(); - try { - Settings.Secure.putStringForUser(mContext.getContentResolver(), - SCREENSAVER_COMPONENTS, - componentsToString(componentNames), - userId); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public ComponentName getDefaultDreamComponent() { - checkPermission(android.Manifest.permission.READ_DREAM_STATE); - int userId = UserHandle.getCallingUserId(); - - final long ident = Binder.clearCallingIdentity(); - try { - String name = Settings.Secure.getStringForUser(mContext.getContentResolver(), - SCREENSAVER_DEFAULT_COMPONENT, - userId); - return name == null ? null : ComponentName.unflattenFromString(name); - } finally { - Binder.restoreCallingIdentity(ident); - } - - } - - @Override - public boolean isDreaming() { - checkPermission(android.Manifest.permission.READ_DREAM_STATE); - - return mIsDreaming; - } - - @Override - public void dream() { - checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); - - final long ident = Binder.clearCallingIdentity(); - try { - if (DEBUG) Slog.v(TAG, "Dream now"); - ComponentName[] dreams = getDreamComponentsForUser(mCurrentUserManager.getCurrentUserId()); - ComponentName firstDream = dreams != null && dreams.length > 0 ? dreams[0] : null; - if (firstDream != null) { - mHandler.requestStart(firstDream, false /*isTest*/); - synchronized (mLock) { - setDreamingLocked(true, false /*isTest*/); - } - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public void testDream(ComponentName dream) { - checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); - - final long ident = Binder.clearCallingIdentity(); - try { - if (DEBUG) Slog.v(TAG, "Test dream name=" + dream); - if (dream != null) { - mHandler.requestStart(dream, true /*isTest*/); - synchronized (mLock) { - setDreamingLocked(true, true /*isTest*/); - } - } - } finally { - Binder.restoreCallingIdentity(ident); - } - - } - - @Override - public void awaken() { - checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); - - final long ident = Binder.clearCallingIdentity(); - try { - if (DEBUG) Slog.v(TAG, "Wake up"); - mHandler.requestStop(); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public void awakenSelf(IBinder token) { - // requires no permission, called by Dream from an arbitrary process - - final long ident = Binder.clearCallingIdentity(); - try { - if (DEBUG) Slog.v(TAG, "Wake up from dream: " + token); - if (token != null) { - mHandler.requestStopSelf(token); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - // end IDreamManager api - - // begin ServiceConnection - @Override - public void onServiceConnected(ComponentName name, IBinder dream) { - if (DEBUG) Slog.v(TAG, "Service connected: " + name + " binder=" + - dream + " thread=" + Thread.currentThread().getId()); - mHandler.requestAttach(name, dream); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - if (DEBUG) Slog.v(TAG, "Service disconnected: " + name); - // Only happens in exceptional circumstances, awaken just to be safe - awaken(); - } - // end ServiceConnection - - private void checkPermission(String permission) { - if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(permission)) { - throw new SecurityException("Access denied to process: " + Binder.getCallingPid() - + ", must have permission " + permission); - } - } - - private void setDreamingLocked(boolean isDreaming, boolean isTest) { - boolean wasDreaming = mIsDreaming; - if (!isTest) { - if (!wasDreaming && isDreaming) { - if (DEBUG) Slog.v(TAG, "Firing ACTION_DREAMING_STARTED"); - mContext.sendBroadcast(mDreamingStartedIntent); - } else if (wasDreaming && !isDreaming) { - if (DEBUG) Slog.v(TAG, "Firing ACTION_DREAMING_STOPPED"); - mContext.sendBroadcast(mDreamingStoppedIntent); - } - } - mIsDreaming = isDreaming; - } - - private ComponentName[] getDreamComponentsForUser(int userId) { - String names = Settings.Secure.getStringForUser(mContext.getContentResolver(), - SCREENSAVER_COMPONENTS, - userId); - return names == null ? null : componentsFromString(names); - } - - private static String componentsToString(ComponentName[] componentNames) { - StringBuilder names = new StringBuilder(); - if (componentNames != null) { - for (ComponentName componentName : componentNames) { - if (names.length() > 0) { - names.append(','); - } - names.append(componentName.flattenToString()); - } - } - return names.toString(); - } - - private static ComponentName[] componentsFromString(String names) { - String[] namesArray = names.split(","); - ComponentName[] componentNames = new ComponentName[namesArray.length]; - for (int i = 0; i < namesArray.length; i++) { - componentNames[i] = ComponentName.unflattenFromString(namesArray[i]); - } - return componentNames; - } - - /** - * Keeps track of the current user, since dream() uses the current user's configuration. - */ - private static class CurrentUserManager { - private final Object mLock = new Object(); - private int mCurrentUserId; - - public void init(Context context) { - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_USER_SWITCHED); - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_USER_SWITCHED.equals(action)) { - synchronized(mLock) { - mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - if (DEBUG) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house"); - } - } - }}, filter); - try { - synchronized (mLock) { - mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id; - } - } catch (RemoteException e) { - Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); - } - } - - public void dump(PrintWriter pw) { - pw.print(" user="); pw.println(getCurrentUserId()); - } - - public int getCurrentUserId() { - synchronized(mLock) { - return mCurrentUserId; - } - } - } - - /** - * Handler for asynchronous operations performed by the dream manager. - * - * Ensures operations to {@link DreamController} are single-threaded. - */ - private static final class DreamControllerHandler extends Handler { - private final DreamController mController; - private final Runnable mStopRunnable = new Runnable() { - @Override - public void run() { - mController.stop(); - }}; - - public DreamControllerHandler(DreamController controller) { - super(true /*async*/); - mController = controller; - } - - public void requestStart(final ComponentName name, final boolean isTest) { - post(new Runnable(){ - @Override - public void run() { - mController.start(name, isTest); - }}); - } - - public void requestAttach(final ComponentName name, final IBinder dream) { - post(new Runnable(){ - @Override - public void run() { - mController.attach(name, dream); - }}); - } - - public void requestStopSelf(final IBinder token) { - post(new Runnable(){ - @Override - public void run() { - mController.stopSelf(token); - }}); - } - - public void requestStop() { - post(mStopRunnable); - } - - } - -} diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 76194ae79be5..5d5f8d37d222 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -890,6 +890,7 @@ public class NotificationManagerService extends INotificationManager.Stub userId = ActivityManager.handleIncomingUser(callingPid, callingUid, userId, true, true, "enqueueNotification", pkg); + final UserHandle user = new UserHandle(userId); // Limit the number of notifications that any given package except the android // package can enqueue. Prevents DOS attacks and deals with leaks. @@ -991,7 +992,6 @@ public class NotificationManagerService extends INotificationManager.Stub } if (notification.icon != 0) { - final UserHandle user = new UserHandle(userId); final StatusBarNotification n = new StatusBarNotification( pkg, id, tag, r.uid, r.initialPid, score, notification, user); if (old != null && old.statusBarKey != null) { @@ -1063,7 +1063,7 @@ public class NotificationManagerService extends INotificationManager.Stub try { final IRingtonePlayer player = mAudioService.getRingtonePlayer(); if (player != null) { - player.playAsync(uri, looping, audioStreamType); + player.playAsync(uri, user, looping, audioStreamType); } } catch (RemoteException e) { } finally { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index eeab757b5e75..738e19b1f267 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -38,6 +38,7 @@ import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.server.search.SearchManagerService; +import android.service.dreams.Dream; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -51,6 +52,7 @@ import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.am.ActivityManagerService; import com.android.server.am.BatteryStatsService; import com.android.server.display.DisplayManagerService; +import com.android.server.dreams.DreamManagerService; import com.android.server.input.InputManagerService; import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; @@ -738,8 +740,8 @@ class ServerThread extends Thread { try { Slog.i(TAG, "Dreams Service"); // Dreams (interactive idle-time views, a/k/a screen savers) - dreamy = new DreamManagerService(context); - ServiceManager.addService("dreams", dreamy); + dreamy = new DreamManagerService(context, wmHandler); + ServiceManager.addService(Dream.DREAM_SERVICE, dreamy); } catch (Throwable e) { reportWtf("starting DreamManagerService", e); } @@ -810,7 +812,7 @@ class ServerThread extends Thread { context.getResources().updateConfiguration(config, metrics); try { - power.systemReady(twilight); + power.systemReady(twilight, dreamy); } catch (Throwable e) { reportWtf("making Power Manager Service ready", e); } diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java index 85a6e4150f29..07e8f182ed70 100644 --- a/services/java/com/android/server/UiModeManagerService.java +++ b/services/java/com/android/server/UiModeManagerService.java @@ -17,6 +17,7 @@ package com.android.server; import android.app.Activity; +import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IUiModeManager; import android.app.Notification; @@ -39,6 +40,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; +import android.service.dreams.Dream; +import android.service.dreams.IDreamManager; import android.util.Slog; import java.io.FileDescriptor; @@ -56,6 +59,9 @@ class UiModeManagerService extends IUiModeManager.Stub { private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true; private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true; + private static final int DEFAULT_SCREENSAVER_ENABLED = 1; + private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1; + private final Context mContext; private final TwilightService mTwilightService; private final Handler mHandler = new Handler(); @@ -110,72 +116,10 @@ class UiModeManagerService extends IUiModeManager.Stub { return; } - final int enableFlags = intent.getIntExtra("enableFlags", 0); - final int disableFlags = intent.getIntExtra("disableFlags", 0); - + final int enableFlags = intent.getIntExtra("enableFlags", 0); + final int disableFlags = intent.getIntExtra("disableFlags", 0); synchronized (mLock) { - // Launch a dock activity - String category = null; - if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) { - // Only launch car home when car mode is enabled and the caller - // has asked us to switch to it. - if (ENABLE_LAUNCH_CAR_DOCK_APP - && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { - category = Intent.CATEGORY_CAR_DOCK; - } - } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) { - // Only launch car home when desk mode is enabled and the caller - // has asked us to switch to it. Currently re-using the car - // mode flag since we don't have a formal API for "desk mode". - if (ENABLE_LAUNCH_DESK_DOCK_APP - && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { - category = Intent.CATEGORY_DESK_DOCK; - } - } else { - // Launch the standard home app if requested. - if ((disableFlags&UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { - category = Intent.CATEGORY_HOME; - } - } - - if (LOG) { - Slog.v(TAG, String.format( - "Handling broadcast result for action %s: enable=0x%08x disable=0x%08x category=%s", - intent.getAction(), enableFlags, disableFlags, category)); - } - - if (category != null) { - // This is the new activity that will serve as home while - // we are in care mode. - Intent homeIntent = buildHomeIntent(category); - - // Now we are going to be careful about switching the - // configuration and starting the activity -- we need to - // do this in a specific order under control of the - // activity manager, to do it cleanly. So compute the - // new config, but don't set it yet, and let the - // activity manager take care of both the start and config - // change. - Configuration newConfig = null; - if (mHoldingConfiguration) { - mHoldingConfiguration = false; - updateConfigurationLocked(false); - newConfig = mConfiguration; - } - try { - ActivityManagerNative.getDefault().startActivityWithConfig( - null, homeIntent, null, null, null, 0, 0, - newConfig, null, UserHandle.USER_CURRENT); - mHoldingConfiguration = false; - } catch (RemoteException e) { - Slog.w(TAG, e.getCause()); - } - } - - if (mHoldingConfiguration) { - mHoldingConfiguration = false; - updateConfigurationLocked(true); - } + updateAfterBroadcastLocked(intent.getAction(), enableFlags, disableFlags); } } }; @@ -335,9 +279,8 @@ class UiModeManagerService extends IUiModeManager.Stub { } } - final void updateConfigurationLocked(boolean sendIt) { - int uiMode = mTelevision ? Configuration.UI_MODE_TYPE_TELEVISION - : mDefaultUiModeType; + final void updateConfigurationLocked() { + int uiMode = mTelevision ? Configuration.UI_MODE_TYPE_TELEVISION : mDefaultUiModeType; if (mCarModeEnabled) { uiMode = Configuration.UI_MODE_TYPE_CAR; } else if (isDeskDockState(mDockState)) { @@ -365,17 +308,19 @@ class UiModeManagerService extends IUiModeManager.Stub { } mCurUiMode = uiMode; - - if (!mHoldingConfiguration && uiMode != mSetUiMode) { - mSetUiMode = uiMode; + if (!mHoldingConfiguration) { mConfiguration.uiMode = uiMode; + } + } - if (sendIt) { - try { - ActivityManagerNative.getDefault().updateConfiguration(mConfiguration); - } catch (RemoteException e) { - Slog.w(TAG, "Failure communicating with activity manager", e); - } + final void sendConfigurationLocked() { + if (mSetUiMode != mConfiguration.uiMode) { + mSetUiMode = mConfiguration.uiMode; + + try { + ActivityManagerNative.getDefault().updateConfiguration(mConfiguration); + } catch (RemoteException e) { + Slog.w(TAG, "Failure communicating with activity manager", e); } } } @@ -434,43 +379,38 @@ class UiModeManagerService extends IUiModeManager.Stub { intent.putExtra("disableFlags", disableFlags); mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, mResultReceiver, null, Activity.RESULT_OK, null, null); + // Attempting to make this transition a little more clean, we are going // to hold off on doing a configuration change until we have finished // the broadcast and started the home activity. mHoldingConfiguration = true; + updateConfigurationLocked(); } else { - Intent homeIntent = null; + String category = null; if (mCarModeEnabled) { if (ENABLE_LAUNCH_CAR_DOCK_APP - && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { - homeIntent = buildHomeIntent(Intent.CATEGORY_CAR_DOCK); + && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + category = Intent.CATEGORY_CAR_DOCK; } } else if (isDeskDockState(mDockState)) { if (ENABLE_LAUNCH_DESK_DOCK_APP - && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { - homeIntent = buildHomeIntent(Intent.CATEGORY_DESK_DOCK); + && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + category = Intent.CATEGORY_DESK_DOCK; } } else { - if ((disableFlags&UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { - homeIntent = buildHomeIntent(Intent.CATEGORY_HOME); + if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { + category = Intent.CATEGORY_HOME; } } if (LOG) { Slog.v(TAG, "updateLocked: null action, mDockState=" - + mDockState +", firing homeIntent: " + homeIntent); + + mDockState +", category=" + category); } - if (homeIntent != null) { - try { - mContext.startActivityAsUser(homeIntent, UserHandle.CURRENT); - } catch (ActivityNotFoundException e) { - } - } + sendConfigurationAndStartDreamOrDockAppLocked(category); } - updateConfigurationLocked(true); - // keep screen on when charging and in car mode boolean keepScreenOn = mCharging && ((mCarModeEnabled && mCarModeKeepsScreenOn) || @@ -487,6 +427,100 @@ class UiModeManagerService extends IUiModeManager.Stub { } } + private void updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags) { + // Launch a dock activity + String category = null; + if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) { + // Only launch car home when car mode is enabled and the caller + // has asked us to switch to it. + if (ENABLE_LAUNCH_CAR_DOCK_APP + && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + category = Intent.CATEGORY_CAR_DOCK; + } + } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(action)) { + // Only launch car home when desk mode is enabled and the caller + // has asked us to switch to it. Currently re-using the car + // mode flag since we don't have a formal API for "desk mode". + if (ENABLE_LAUNCH_DESK_DOCK_APP + && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + category = Intent.CATEGORY_DESK_DOCK; + } + } else { + // Launch the standard home app if requested. + if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { + category = Intent.CATEGORY_HOME; + } + } + + if (LOG) { + Slog.v(TAG, String.format( + "Handling broadcast result for action %s: enable=0x%08x, disable=0x%08x, " + + "category=%s", + action, enableFlags, disableFlags, category)); + } + + sendConfigurationAndStartDreamOrDockAppLocked(category); + } + + private void sendConfigurationAndStartDreamOrDockAppLocked(String category) { + // Update the configuration but don't send it yet. + mHoldingConfiguration = false; + updateConfigurationLocked(); + + // Start the dock app, if there is one. + boolean dockAppStarted = false; + if (category != null) { + // Now we are going to be careful about switching the + // configuration and starting the activity -- we need to + // do this in a specific order under control of the + // activity manager, to do it cleanly. So compute the + // new config, but don't set it yet, and let the + // activity manager take care of both the start and config + // change. + Intent homeIntent = buildHomeIntent(category); + try { + int result = ActivityManagerNative.getDefault().startActivityWithConfig( + null, homeIntent, null, null, null, 0, 0, + mConfiguration, null, UserHandle.USER_CURRENT); + if (result >= ActivityManager.START_SUCCESS) { + dockAppStarted = true; + } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) { + Slog.e(TAG, "Could not start dock app: " + homeIntent + + ", startActivityWithConfig result " + result); + } + } catch (RemoteException ex) { + Slog.e(TAG, "Could not start dock app: " + homeIntent, ex); + } + } + + // Send the new configuration. + sendConfigurationLocked(); + + // If we did not start a dock app, then start dreaming if supported. + if (!dockAppStarted && isScreenSaverEnabled() && isScreenSaverActivatedOnDock()) { + Slog.i(TAG, "Activating dream while docked."); + try { + IDreamManager dreamManagerService = IDreamManager.Stub.asInterface( + ServiceManager.getService(Dream.DREAM_SERVICE)); + dreamManagerService.dream(); + } catch (RemoteException ex) { + Slog.e(TAG, "Could not start dream when docked.", ex); + } + } + } + + private boolean isScreenSaverEnabled() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED, + UserHandle.USER_CURRENT) != 0; + } + + private boolean isScreenSaverActivatedOnDock() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, + DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK, UserHandle.USER_CURRENT) != 0; + } + private void adjustStatusBarCarModeLocked() { if (mStatusBarManager == null) { mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE); diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index 0c0f00c44126..1269433d0a6f 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -44,7 +44,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; -import android.content.pm.UserInfo; import android.os.Binder; import android.os.IBinder; import android.os.Message; @@ -391,7 +390,7 @@ public class ActiveServices { if (r.isForeground) { r.isForeground = false; if (r.app != null) { - mAm.updateLruProcessLocked(r.app, false, true); + mAm.updateLruProcessLocked(r.app, false); updateServiceForegroundLocked(r.app, true); } } @@ -760,7 +759,8 @@ public class ActiveServices { int N = mPendingServices.size(); for (int i=0; i<N; i++) { ServiceRecord pr = mPendingServices.get(i); - if (pr.name.equals(name)) { + if (pr.serviceInfo.applicationInfo.uid == sInfo.applicationInfo.uid + && pr.name.equals(name)) { mPendingServices.remove(i); i--; N--; @@ -942,7 +942,7 @@ public class ActiveServices { Slog.w(TAG, "Scheduling restart of crashed service " + r.shortName + " in " + r.restartDelay + "ms"); EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART, - r.shortName, r.restartDelay); + r.userId, r.shortName, r.restartDelay); return canceled; } @@ -1083,14 +1083,14 @@ public class ActiveServices { app.services.add(r); bumpServiceExecutingLocked(r, "create"); - mAm.updateLruProcessLocked(app, true, true); + mAm.updateLruProcessLocked(app, true); boolean created = false; try { mAm.mStringBuilder.setLength(0); r.intent.getIntent().toShortString(mAm.mStringBuilder, true, false, true, false); EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE, - System.identityHashCode(r), r.shortName, + r.userId, System.identityHashCode(r), r.shortName, mAm.mStringBuilder.toString(), r.app.pid); synchronized (r.stats.getBatteryStats()) { r.stats.startLaunchedLocked(); @@ -1240,7 +1240,7 @@ public class ActiveServices { if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent); EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE, - System.identityHashCode(r), r.shortName, + r.userId, System.identityHashCode(r), r.shortName, (r.app != null) ? r.app.pid : -1); mServiceMap.removeServiceByName(r.name, r.userId); @@ -1664,7 +1664,7 @@ public class ActiveServices { Slog.w(TAG, "Service crashed " + sr.crashCount + " times, stopping: " + sr); EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH, - sr.crashCount, sr.shortName, app.pid); + sr.userId, sr.crashCount, sr.shortName, app.pid); bringDownServiceLocked(sr, true); } else if (!allowRestart) { bringDownServiceLocked(sr, true); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index b266bd4df121..370d4270fb15 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1369,7 +1369,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (mSelf.mPidsSelfLocked) { mSelf.mPidsSelfLocked.put(app.pid, app); } - mSelf.updateLruProcessLocked(app, true, true); + mSelf.updateLruProcessLocked(app, true); } } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException( @@ -1805,8 +1805,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private final void updateLruProcessInternalLocked(ProcessRecord app, - boolean updateActivityTime, int bestPos) { + private final void updateLruProcessInternalLocked(ProcessRecord app, int bestPos) { // put it on the LRU to keep track of when it should be exited. int lrui = mLruProcesses.indexOf(app); if (lrui >= 0) mLruProcesses.remove(lrui); @@ -1817,9 +1816,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.lruSeq = mLruSeq; // compute the new weight for this process. - if (updateActivityTime) { - app.lastActivityTime = SystemClock.uptimeMillis(); - } + app.lastActivityTime = SystemClock.uptimeMillis(); if (app.activities.size() > 0) { // If this process has activities, we more strongly want to keep // it around. @@ -1863,24 +1860,22 @@ public final class ActivityManagerService extends ActivityManagerNative if (cr.binding != null && cr.binding.service != null && cr.binding.service.app != null && cr.binding.service.app.lruSeq != mLruSeq) { - updateLruProcessInternalLocked(cr.binding.service.app, - updateActivityTime, i+1); + updateLruProcessInternalLocked(cr.binding.service.app, i+1); } } } for (int j=app.conProviders.size()-1; j>=0; j--) { ContentProviderRecord cpr = app.conProviders.get(j).provider; if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) { - updateLruProcessInternalLocked(cpr.proc, - updateActivityTime, i+1); + updateLruProcessInternalLocked(cpr.proc, i+1); } } } final void updateLruProcessLocked(ProcessRecord app, - boolean oomAdj, boolean updateActivityTime) { + boolean oomAdj) { mLruSeq++; - updateLruProcessInternalLocked(app, updateActivityTime, 0); + updateLruProcessInternalLocked(app, 0); //Slog.i(TAG, "Putting proc to front: " + app.processName); if (oomAdj) { @@ -1981,7 +1976,8 @@ public final class ActivityManagerService extends ActivityManagerNative + "/" + info.processName); mProcessCrashTimes.remove(info.processName, info.uid); if (mBadProcesses.get(info.processName, info.uid) != null) { - EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid, + EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, + UserHandle.getUserId(info.uid), info.uid, info.processName); mBadProcesses.remove(info.processName, info.uid); if (app != null) { @@ -2129,7 +2125,8 @@ public final class ActivityManagerService extends ActivityManagerNative } } - EventLog.writeEvent(EventLogTags.AM_PROC_START, startResult.pid, uid, + EventLog.writeEvent(EventLogTags.AM_PROC_START, + UserHandle.getUserId(uid), startResult.pid, uid, app.processName, hostingType, hostingNameStr != null ? hostingNameStr : ""); @@ -2946,7 +2943,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (!r.finishing) { Slog.w(TAG, "Force removing " + r + ": app died, no saved state"); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, - System.identityHashCode(r), + r.userId, System.identityHashCode(r), r.task.taskId, r.shortComponentName, "proc died without state saved"); } @@ -3037,7 +3034,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died."); } - EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.pid, app.processName); + EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName); if (localLOGV) Slog.v( TAG, "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder()); @@ -3086,7 +3083,7 @@ public final class ActivityManagerService extends ActivityManagerNative // A new process has already been started. Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died and restarted (pid " + app.pid + ")."); - EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.pid, app.processName); + EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName); } else if (DEBUG_PROCESSES) { Slog.d(TAG, "Received spurious death notification for thread " + thread.asBinder()); @@ -3321,8 +3318,8 @@ public final class ActivityManagerService extends ActivityManagerNative app.notResponding = true; // Log the ANR to the event log. - EventLog.writeEvent(EventLogTags.AM_ANR, app.pid, app.processName, app.info.flags, - annotation); + EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid, + app.processName, app.info.flags, annotation); // Dump thread traces as quickly as we can, starting with "interesting" processes. firstPids.add(app.pid); @@ -3408,7 +3405,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { Slog.w(TAG, "Killing " + app + ": background ANR"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "background ANR"); Process.killProcessQuiet(app.pid); return; @@ -4077,8 +4074,8 @@ public final class ActivityManagerService extends ActivityManagerNative if (gone) { Slog.w(TAG, "Process " + app + " failed to attach"); - EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.uid, - app.processName); + EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId, + pid, app.uid, app.processName); mProcessNames.remove(app.processName, app.uid); mIsolatedProcesses.remove(app.uid); if (mHeavyWeightProcess == app) { @@ -4090,7 +4087,7 @@ public final class ActivityManagerService extends ActivityManagerNative checkAppInLaunchingProvidersLocked(app, true); // Take care of any services that are waiting for the process. mServices.processStartTimedOutLocked(app); - EventLog.writeEvent(EventLogTags.AM_KILL, pid, + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, pid, app.processName, app.setAdj, "start timeout"); Process.killProcessQuiet(pid); if (mBackupTarget != null && mBackupTarget.app.pid == pid) { @@ -4166,7 +4163,7 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } - EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.pid, app.processName); + EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName); app.thread = thread; app.curAdj = app.setAdj = -100; @@ -4244,7 +4241,7 @@ public final class ActivityManagerService extends ActivityManagerNative enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent, new Configuration(mConfiguration), app.compat, getCommonServicesLocked(), mCoreSettingsObserver.getCoreSettingsLocked()); - updateLruProcessLocked(app, false, true); + updateLruProcessLocked(app, false); app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis(); } catch (Exception e) { // todo: Yikes! What should we do? For now we will try to @@ -5914,7 +5911,7 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord pr = procs.get(i); if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { Slog.i(TAG, "Killing " + pr.toShortString() + ": remove task"); - EventLog.writeEvent(EventLogTags.AM_KILL, pr.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, pr.userId, pr.pid, pr.processName, pr.setAdj, "remove task"); pr.killedBackground = true; Process.killProcessQuiet(pr.pid); @@ -6442,7 +6439,7 @@ public final class ActivityManagerService extends ActivityManagerNative // make sure to count it as being accessed and thus // back up on the LRU list. This is good because // content providers are often expensive to start. - updateLruProcessLocked(cpr.proc, false, true); + updateLruProcessLocked(cpr.proc, false); } } @@ -6630,6 +6627,7 @@ public final class ActivityManagerService extends ActivityManagerNative + cpi.applicationInfo.uid + " for provider " + name + ": launching app became null"); EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS, + UserHandle.getUserId(cpi.applicationInfo.uid), cpi.applicationInfo.packageName, cpi.applicationInfo.uid, name); return null; @@ -7013,7 +7011,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (isolated) { mIsolatedProcesses.put(app.uid, app); } - updateLruProcessLocked(app, true, true); + updateLruProcessLocked(app, true); } // This package really, really can not be stopped. @@ -7499,7 +7497,7 @@ public final class ActivityManagerService extends ActivityManagerNative int adj = proc.setAdj; if (adj >= worstType && !proc.killedBackground) { Slog.w(TAG, "Killing " + proc + " (adj " + adj + "): " + reason); - EventLog.writeEvent(EventLogTags.AM_KILL, proc.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, proc.userId, proc.pid, proc.processName, adj, reason); killed = true; proc.killedBackground = true; @@ -7535,8 +7533,8 @@ public final class ActivityManagerService extends ActivityManagerNative final int adj = proc.setAdj; if (adj > belowAdj && !proc.killedBackground) { Slog.w(TAG, "Killing " + proc + " (adj " + adj + "): " + reason); - EventLog.writeEvent( - EventLogTags.AM_KILL, proc.pid, proc.processName, adj, reason); + EventLog.writeEvent(EventLogTags.AM_KILL, proc.userId, + proc.pid, proc.processName, adj, reason); killed = true; proc.killedBackground = true; Process.killProcessQuiet(pid); @@ -7953,7 +7951,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.pid > 0 && app.pid != MY_PID) { handleAppCrashLocked(app); Slog.i(ActivityManagerService.TAG, "Killing " + app + ": user's request"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "user's request after error"); Process.killProcessQuiet(app.pid); } @@ -7978,7 +7976,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "Process " + app.info.processName + " has crashed too many times: killing!"); EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH, - app.info.processName, app.uid); + app.userId, app.info.processName, app.uid); for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if (r.app == app) { @@ -7993,7 +7991,7 @@ public final class ActivityManagerService extends ActivityManagerNative // explicitly does so... but for persistent process, we really // need to keep it running. If a persistent process is actually // repeatedly crashing, then badness for everyone. - EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.uid, + EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid, app.info.processName); if (!app.isolated) { // XXX We don't have a way to mark isolated processes @@ -8106,7 +8104,7 @@ public final class ActivityManagerService extends ActivityManagerNative : (r == null ? "unknown" : r.processName); EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(), - processName, + UserHandle.getUserId(Binder.getCallingUid()), processName, r == null ? -1 : r.info.flags, crashInfo.exceptionClassName, crashInfo.exceptionMessage, @@ -8304,7 +8302,8 @@ public final class ActivityManagerService extends ActivityManagerNative final String processName = app == null ? "system_server" : (r == null ? "unknown" : r.processName); - EventLog.writeEvent(EventLogTags.AM_WTF, Binder.getCallingPid(), + EventLog.writeEvent(EventLogTags.AM_WTF, + UserHandle.getUserId(Binder.getCallingUid()), Binder.getCallingPid(), processName, r == null ? -1 : r.info.flags, tag, crashInfo.exceptionMessage); @@ -10067,6 +10066,7 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(" "); pw.print("oom: max="); pw.print(r.maxAdj); pw.print(" hidden="); pw.print(r.hiddenAdj); + pw.print(" client="); pw.print(r.clientHiddenAdj); pw.print(" empty="); pw.print(r.emptyAdj); pw.print(" curRaw="); pw.print(r.curRawAdj); pw.print(" setRaw="); pw.print(r.setRawAdj); @@ -10591,7 +10591,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.i(TAG, "Kill " + capp.processName + " (pid " + capp.pid + "): provider " + cpr.info.name + " in dying process " + (proc != null ? proc.processName : "??")); - EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, capp.userId, capp.pid, capp.processName, capp.setAdj, "dying provider " + cpr.name.toShortString()); Process.killProcessQuiet(capp.pid); @@ -12466,7 +12466,7 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } - private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj, + private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj, int clientHiddenAdj, int emptyAdj, ProcessRecord TOP_APP, boolean recursed, boolean doingAll) { if (mAdjSeq == app.adjSeq) { // This adjustment has already been computed. If we are calling @@ -12474,8 +12474,13 @@ public final class ActivityManagerService extends ActivityManagerNative // an earlier hidden adjustment that isn't really for us... if // so, use the new hidden adjustment. if (!recursed && app.hidden) { - app.curAdj = app.curRawAdj = app.nonStoppingAdj = - app.hasActivities ? hiddenAdj : emptyAdj; + if (app.hasActivities) { + app.curAdj = app.curRawAdj = app.nonStoppingAdj = hiddenAdj; + } else if (app.hasClientActivities) { + app.curAdj = app.curRawAdj = app.nonStoppingAdj = clientHiddenAdj; + } else { + app.curAdj = app.curRawAdj = app.nonStoppingAdj = emptyAdj; + } } return app.curRawAdj; } @@ -12491,6 +12496,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjTarget = null; app.empty = false; app.hidden = false; + app.hasClientActivities = false; final int activitiesSize = app.activities.size(); @@ -12572,7 +12578,7 @@ public final class ActivityManagerService extends ActivityManagerNative adj = hiddenAdj; schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; app.hidden = true; - app.adjType = "bg-activities"; + app.adjType = "bg-act"; } boolean hasStoppingActivities = false; @@ -12614,11 +12620,16 @@ public final class ActivityManagerService extends ActivityManagerNative } if (adj == hiddenAdj && !app.hasActivities) { - // Whoops, this process is completely empty as far as we know - // at this point. - adj = emptyAdj; - app.empty = true; - app.adjType = "bg-empty"; + if (app.hasClientActivities) { + adj = clientHiddenAdj; + app.adjType = "bg-client-act"; + } else { + // Whoops, this process is completely empty as far as we know + // at this point. + adj = emptyAdj; + app.empty = true; + app.adjType = "bg-empty"; + } } if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -12626,13 +12637,13 @@ public final class ActivityManagerService extends ActivityManagerNative // The user is aware of this app, so make it visible. adj = ProcessList.PERCEPTIBLE_APP_ADJ; app.hidden = false; - app.adjType = "foreground-service"; + app.adjType = "fg-service"; schedGroup = Process.THREAD_GROUP_DEFAULT; } else if (app.forcingToForeground != null) { // The user is aware of this app, so make it visible. adj = ProcessList.PERCEPTIBLE_APP_ADJ; app.hidden = false; - app.adjType = "force-foreground"; + app.adjType = "force-fg"; app.adjSource = app.forcingToForeground; schedGroup = Process.THREAD_GROUP_DEFAULT; } @@ -12754,6 +12765,14 @@ public final class ActivityManagerService extends ActivityManagerNative myHiddenAdj = ProcessList.VISIBLE_APP_ADJ; } } + int myClientHiddenAdj = clientHiddenAdj; + if (myClientHiddenAdj > client.clientHiddenAdj) { + if (client.clientHiddenAdj >= ProcessList.VISIBLE_APP_ADJ) { + myClientHiddenAdj = client.clientHiddenAdj; + } else { + myClientHiddenAdj = ProcessList.VISIBLE_APP_ADJ; + } + } int myEmptyAdj = emptyAdj; if (myEmptyAdj > client.emptyAdj) { if (client.emptyAdj >= ProcessList.VISIBLE_APP_ADJ) { @@ -12763,7 +12782,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } clientAdj = computeOomAdjLocked(client, myHiddenAdj, - myEmptyAdj, TOP_APP, true, doingAll); + myClientHiddenAdj, myEmptyAdj, TOP_APP, true, doingAll); String adjType = null; if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) { // Not doing bind OOM management, so treat @@ -12792,6 +12811,19 @@ public final class ActivityManagerService extends ActivityManagerNative clientAdj = adj; } } + } else if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) { + if ((cr.flags&Context.BIND_NOT_VISIBLE) == 0) { + // If this connection is keeping the service + // created, then we want to try to better follow + // its memory management semantics for activities. + // That is, if it is sitting in the background + // LRU list as a hidden process (with activities), + // we don't want the service it is connected to + // to go into the empty LRU and quickly get killed, + // because I'll we'll do is just end up restarting + // the service. + app.hasClientActivities |= client.hasActivities; + } } if (adj > clientAdj) { // If this process has recently shown UI, and @@ -12843,8 +12875,8 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + final ActivityRecord a = cr.activity; if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) { - ActivityRecord a = cr.activity; if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && (a.visible || a.state == ActivityState.RESUMED || a.state == ActivityState.PAUSING)) { @@ -12902,6 +12934,14 @@ public final class ActivityManagerService extends ActivityManagerNative myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ; } } + int myClientHiddenAdj = clientHiddenAdj; + if (myClientHiddenAdj > client.clientHiddenAdj) { + if (client.clientHiddenAdj >= ProcessList.FOREGROUND_APP_ADJ) { + myClientHiddenAdj = client.clientHiddenAdj; + } else { + myClientHiddenAdj = ProcessList.FOREGROUND_APP_ADJ; + } + } int myEmptyAdj = emptyAdj; if (myEmptyAdj > client.emptyAdj) { if (client.emptyAdj > ProcessList.FOREGROUND_APP_ADJ) { @@ -12911,7 +12951,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } int clientAdj = computeOomAdjLocked(client, myHiddenAdj, - myEmptyAdj, TOP_APP, true, doingAll); + myClientHiddenAdj, myEmptyAdj, TOP_APP, true, doingAll); if (adj > clientAdj) { if (app.hasShownUi && app != mHomeProcess && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -13301,7 +13341,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "Excessive wake lock in " + app.processName + " (pid " + app.pid + "): held " + wtimeUsed + " during " + realtimeSince); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "excessive wake lock"); Process.killProcessQuiet(app.pid); } else if (doCpuKills && uptimeSince > 0 @@ -13313,7 +13353,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "Excessive CPU in " + app.processName + " (pid " + app.pid + "): used " + cputimeUsed + " during " + uptimeSince); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "excessive cpu"); Process.killProcessQuiet(app.pid); } else { @@ -13325,8 +13365,9 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean updateOomAdjLocked(ProcessRecord app, int hiddenAdj, - int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) { + int clientHiddenAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) { app.hiddenAdj = hiddenAdj; + app.clientHiddenAdj = clientHiddenAdj; app.emptyAdj = emptyAdj; if (app.thread == null) { @@ -13337,7 +13378,7 @@ public final class ActivityManagerService extends ActivityManagerNative boolean success = true; - computeOomAdjLocked(app, hiddenAdj, emptyAdj, TOP_APP, false, doingAll); + computeOomAdjLocked(app, hiddenAdj, clientHiddenAdj, emptyAdj, TOP_APP, false, doingAll); if (app.curRawAdj != app.setRawAdj) { if (wasKeeping && !app.keeping) { @@ -13374,7 +13415,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.waitingToKill != null && app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { Slog.i(TAG, "Killing " + app.toShortString() + ": " + app.waitingToKill); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, app.waitingToKill); app.killedBackground = true; Process.killProcessQuiet(app.pid); @@ -13424,8 +13465,8 @@ public final class ActivityManagerService extends ActivityManagerNative mAdjSeq++; - boolean success = updateOomAdjLocked(app, app.hiddenAdj, app.emptyAdj, - TOP_APP, false); + boolean success = updateOomAdjLocked(app, app.hiddenAdj, app.clientHiddenAdj, + app.emptyAdj, TOP_APP, false); final boolean nowHidden = app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ && app.curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ; if (nowHidden != wasHidden) { @@ -13439,6 +13480,7 @@ public final class ActivityManagerService extends ActivityManagerNative final void updateOomAdjLocked() { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; + final long oldTime = SystemClock.uptimeMillis() - ProcessList.MAX_EMPTY_TIME; if (false) { RuntimeException e = new RuntimeException(); @@ -13449,20 +13491,40 @@ public final class ActivityManagerService extends ActivityManagerNative mAdjSeq++; mNewNumServiceProcs = 0; + final int emptyProcessLimit; + final int hiddenProcessLimit; + if (mProcessLimit <= 0) { + emptyProcessLimit = hiddenProcessLimit = 0; + } else if (mProcessLimit == 1) { + emptyProcessLimit = 1; + hiddenProcessLimit = 0; + } else { + emptyProcessLimit = (mProcessLimit*2)/3; + hiddenProcessLimit = mProcessLimit - emptyProcessLimit; + } + // Let's determine how many processes we have running vs. // how many slots we have for background processes; we may want // to put multiple processes in a slot of there are enough of // them. int numSlots = (ProcessList.HIDDEN_APP_MAX_ADJ - ProcessList.HIDDEN_APP_MIN_ADJ + 1) / 2; - int emptyFactor = (mLruProcesses.size()-mNumNonHiddenProcs-mNumHiddenProcs)/numSlots; + int numEmptyProcs = mLruProcesses.size()-mNumNonHiddenProcs-mNumHiddenProcs; + if (numEmptyProcs > hiddenProcessLimit) { + // If there are more empty processes than our limit on hidden + // processes, then use the hidden process limit for the factor. + // This ensures that the really old empty processes get pushed + // down to the bottom, so if we are running low on memory we will + // have a better chance at keeping around more hidden processes + // instead of a gazillion empty processes. + numEmptyProcs = hiddenProcessLimit; + } + int emptyFactor = numEmptyProcs/numSlots; if (emptyFactor < 1) emptyFactor = 1; int hiddenFactor = (mNumHiddenProcs > 0 ? mNumHiddenProcs : 1)/numSlots; if (hiddenFactor < 1) hiddenFactor = 1; int stepHidden = 0; int stepEmpty = 0; - final int emptyProcessLimit = mProcessLimit > 1 ? mProcessLimit / 2 : mProcessLimit; - final int hiddenProcessLimit = mProcessLimit > 1 ? mProcessLimit / 2 : mProcessLimit; int numHidden = 0; int numEmpty = 0; int numTrimming = 0; @@ -13477,11 +13539,12 @@ public final class ActivityManagerService extends ActivityManagerNative int nextHiddenAdj = curHiddenAdj+1; int curEmptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ; int nextEmptyAdj = curEmptyAdj+2; + int curClientHiddenAdj = curEmptyAdj; while (i > 0) { i--; ProcessRecord app = mLruProcesses.get(i); //Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj); - updateOomAdjLocked(app, curHiddenAdj, curEmptyAdj, TOP_APP, true); + updateOomAdjLocked(app, curHiddenAdj, curClientHiddenAdj, curEmptyAdj, TOP_APP, true); if (!app.killedBackground) { if (app.curRawAdj == curHiddenAdj && app.hasActivities) { // This process was assigned as a hidden process... step the @@ -13496,17 +13559,31 @@ public final class ActivityManagerService extends ActivityManagerNative if (nextHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) { nextHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ; } + if (curClientHiddenAdj <= curHiddenAdj) { + curClientHiddenAdj = curHiddenAdj + 1; + if (curClientHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) { + curClientHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ; + } + } } } numHidden++; if (numHidden > hiddenProcessLimit) { Slog.i(TAG, "No longer want " + app.processName + " (pid " + app.pid + "): hidden #" + numHidden); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "too many background"); app.killedBackground = true; Process.killProcessQuiet(app.pid); } + } else if (app.curRawAdj == curHiddenAdj && app.hasClientActivities) { + // This process has a client that has activities. We will have + // given it the current hidden adj; here we will just leave it + // without stepping the hidden adj. + curClientHiddenAdj++; + if (curClientHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) { + curClientHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ; + } } else { if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curHiddenAdj) { // This process was assigned as an empty process... step the @@ -13525,15 +13602,28 @@ public final class ActivityManagerService extends ActivityManagerNative } else if (app.curRawAdj < ProcessList.HIDDEN_APP_MIN_ADJ) { mNumNonHiddenProcs++; } - if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) { - numEmpty++; - if (numEmpty > emptyProcessLimit) { + if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ + && !app.hasClientActivities) { + if (numEmpty > ProcessList.TRIM_EMPTY_APPS + && app.lastActivityTime < oldTime) { Slog.i(TAG, "No longer want " + app.processName - + " (pid " + app.pid + "): empty #" + numEmpty); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, - app.processName, app.setAdj, "too many background"); + + " (pid " + app.pid + "): empty for " + + ((oldTime+ProcessList.MAX_EMPTY_TIME-app.lastActivityTime) + / 1000) + "s"); + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, + app.processName, app.setAdj, "old background process"); app.killedBackground = true; Process.killProcessQuiet(app.pid); + } else { + numEmpty++; + if (numEmpty > emptyProcessLimit) { + Slog.i(TAG, "No longer want " + app.processName + + " (pid " + app.pid + "): empty #" + numEmpty); + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, + app.processName, app.setAdj, "too many background"); + app.killedBackground = true; + Process.killProcessQuiet(app.pid); + } } } } @@ -13546,7 +13636,7 @@ public final class ActivityManagerService extends ActivityManagerNative // left sitting around after no longer needed. Slog.i(TAG, "Isolated process " + app.processName + " (pid " + app.pid + ") no longer needed"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "isolated not needed"); app.killedBackground = true; Process.killProcessQuiet(app.pid); @@ -13567,8 +13657,8 @@ public final class ActivityManagerService extends ActivityManagerNative // are managing to keep around is less than half the maximum we desire; // if we are keeping a good number around, we'll let them use whatever // memory they want. - if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/4) - && numEmpty <= (ProcessList.MAX_HIDDEN_APPS/4)) { + if (numHidden <= ProcessList.TRIM_HIDDEN_APPS + && numEmpty <= ProcessList.TRIM_EMPTY_APPS) { final int numHiddenAndEmpty = numHidden + numEmpty; final int N = mLruProcesses.size(); int factor = numTrimming/3; @@ -13578,9 +13668,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (factor < minFactor) factor = minFactor; int step = 0; int fgTrimLevel; - if (numHiddenAndEmpty <= (ProcessList.MAX_HIDDEN_APPS/5)) { + if (numHiddenAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; - } else if (numHiddenAndEmpty <= (ProcessList.MAX_HIDDEN_APPS/3)) { + } else if (numHiddenAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; } else { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; @@ -13700,7 +13790,7 @@ public final class ActivityManagerService extends ActivityManagerNative + (app.thread != null ? app.thread.asBinder() : null) + ")\n"); if (app.pid > 0 && app.pid != MY_PID) { - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "empty"); Process.killProcessQuiet(app.pid); } else { diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 7ff574838df0..6cd86fd05d44 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -746,8 +746,8 @@ final class ActivityRecord { final long totalTime = stack.mInitialStartTime != 0 ? (curTime - stack.mInitialStartTime) : thisTime; if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) { - EventLog.writeEvent(EventLogTags.ACTIVITY_LAUNCH_TIME, - System.identityHashCode(this), shortComponentName, + EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME, + userId, System.identityHashCode(this), shortComponentName, thisTime, totalTime); StringBuilder sb = service.mStringBuilder; sb.setLength(0); @@ -923,6 +923,8 @@ final class ActivityRecord { StringBuilder sb = new StringBuilder(128); sb.append("ActivityRecord{"); sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" u"); + sb.append(userId); sb.append(' '); sb.append(intent.getComponent().flattenToShortString()); sb.append('}'); diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 1707ff0fa63b..2d44527421ff 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -638,7 +638,7 @@ final class ActivityStack { if (idx < 0) { app.activities.add(r); } - mService.updateLruProcessLocked(app, true, true); + mService.updateLruProcessLocked(app, true); try { if (app.thread == null) { @@ -656,7 +656,7 @@ final class ActivityStack { + " andResume=" + andResume); if (andResume) { EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, - System.identityHashCode(r), + r.userId, System.identityHashCode(r), r.task.taskId, r.shortComponentName); } if (r.isHomeActivity) { @@ -951,7 +951,7 @@ final class ActivityStack { if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev); try { EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY, - System.identityHashCode(prev), + prev.userId, System.identityHashCode(prev), prev.shortComponentName); prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing, userLeaving, prev.configChangeFlags); @@ -1040,7 +1040,7 @@ final class ActivityStack { completePauseLocked(); } else { EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, - System.identityHashCode(r), r.shortComponentName, + r.userId, System.identityHashCode(r), r.shortComponentName, mPausingActivity != null ? mPausingActivity.shortComponentName : "(none)"); } @@ -1505,7 +1505,7 @@ final class ActivityStack { if (next.app != null && next.app.thread != null) { // No reason to do full oom adj update here; we'll let that // happen whenever it needs to later. - mService.updateLruProcessLocked(next.app, false, true); + mService.updateLruProcessLocked(next.app, false); } startPausingLocked(userLeaving, false); return true; @@ -1641,7 +1641,7 @@ final class ActivityStack { if (mMainStack) { mService.addRecentTaskLocked(next.task); } - mService.updateLruProcessLocked(next.app, true, true); + mService.updateLruProcessLocked(next.app, true); updateLRUListLocked(next); // Have the window manager re-evaluate the orientation of @@ -1699,7 +1699,7 @@ final class ActivityStack { } EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, - System.identityHashCode(next), + next.userId, System.identityHashCode(next), next.task.taskId, next.shortComponentName); next.sleeping = false; @@ -2967,7 +2967,7 @@ final class ActivityStack { intent, r.getUriPermissionsLocked()); if (newTask) { - EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId); + EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId); } logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task); startActivityLocked(r, newTask, doResume, keepCurTransition, options); @@ -3700,7 +3700,7 @@ final class ActivityStack { r.makeFinishing(); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, - System.identityHashCode(r), + r.userId, System.identityHashCode(r), r.task.taskId, r.shortComponentName, reason); if (index < (mHistory.size()-1)) { ActivityRecord next = mHistory.get(index+1); @@ -3996,7 +3996,7 @@ final class ActivityStack { TAG, "Removing activity from " + reason + ": token=" + r + ", app=" + (r.app != null ? r.app.processName : "(null)")); EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, - System.identityHashCode(r), + r.userId, System.identityHashCode(r), r.task.taskId, r.shortComponentName, reason); boolean removedFromHistory = false; @@ -4228,7 +4228,7 @@ final class ActivityStack { } finishTaskMoveLocked(task); - EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task); + EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, task); } private final void finishTaskMoveLocked(int task) { @@ -4448,7 +4448,7 @@ final class ActivityStack { private final void logStartActivity(int tag, ActivityRecord r, TaskRecord task) { EventLog.writeEvent(tag, - System.identityHashCode(r), task.taskId, + r.userId, System.identityHashCode(r), task.taskId, r.shortComponentName, r.intent.getAction(), r.intent.getType(), r.intent.getDataString(), r.intent.getFlags()); @@ -4590,7 +4590,7 @@ final class ActivityStack { + " with results=" + results + " newIntents=" + newIntents + " andResume=" + andResume); EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY - : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r), + : EventLogTags.AM_RELAUNCH_ACTIVITY, r.userId, System.identityHashCode(r), r.task.taskId, r.shortComponentName); r.startFreezingScreenLocked(r.app, 0); diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java index 07440b544a1e..c631b6e0f9d9 100644 --- a/services/java/com/android/server/am/BroadcastFilter.java +++ b/services/java/com/android/server/am/BroadcastFilter.java @@ -64,6 +64,8 @@ class BroadcastFilter extends IntentFilter { StringBuilder sb = new StringBuilder(); sb.append("BroadcastFilter{"); sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" u"); + sb.append(owningUserId); sb.append(' '); sb.append(receiverList); sb.append('}'); diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index b0af081fb0ce..9f27994a77d9 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -209,7 +209,7 @@ public class BroadcastQueue { r.receiver = app.thread.asBinder(); r.curApp = app; app.curReceiver = r; - mService.updateLruProcessLocked(app, true, true); + mService.updateLruProcessLocked(app, true); // Tell the application to launch this receiver. r.intent.setComponent(r.curComponent); @@ -930,22 +930,22 @@ public class BroadcastQueue { if (curReceiver instanceof BroadcastFilter) { BroadcastFilter bf = (BroadcastFilter) curReceiver; EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER, - System.identityHashCode(r), + bf.owningUserId, System.identityHashCode(r), r.intent.getAction(), r.nextReceiver - 1, System.identityHashCode(bf)); } else { + ResolveInfo ri = (ResolveInfo)curReceiver; EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP, - System.identityHashCode(r), - r.intent.getAction(), - r.nextReceiver - 1, - ((ResolveInfo)curReceiver).toString()); + UserHandle.getUserId(ri.activityInfo.applicationInfo.uid), + System.identityHashCode(r), r.intent.getAction(), + r.nextReceiver - 1, ri.toString()); } } else { Slog.w(TAG, "Discarding broadcast before first receiver is invoked: " + r); EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP, - System.identityHashCode(r), + -1, System.identityHashCode(r), r.intent.getAction(), r.nextReceiver, "NONE"); diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index ca6d5f70192b..85ec328d134c 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -194,6 +194,6 @@ class BroadcastRecord extends Binder { public String toString() { return "BroadcastRecord{" + Integer.toHexString(System.identityHashCode(this)) - + " " + intent.getAction() + "}"; + + " u" + userId + " " + intent.getAction() + "}"; } } diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java index 5b3ff8d3986d..4ed3c3150b5a 100644 --- a/services/java/com/android/server/am/ConnectionRecord.java +++ b/services/java/com/android/server/am/ConnectionRecord.java @@ -62,6 +62,8 @@ class ConnectionRecord { StringBuilder sb = new StringBuilder(128); sb.append("ConnectionRecord{"); sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" u"); + sb.append(binding.client.userId); sb.append(' '); if ((flags&Context.BIND_AUTO_CREATE) != 0) { sb.append("CR "); @@ -70,7 +72,7 @@ class ConnectionRecord { sb.append("DBG "); } if ((flags&Context.BIND_NOT_FOREGROUND) != 0) { - sb.append("NOTFG "); + sb.append("!FG "); } if ((flags&Context.BIND_ABOVE_CLIENT) != 0) { sb.append("ABCLT "); @@ -88,7 +90,10 @@ class ConnectionRecord { sb.append("ACT "); } if ((flags&Context.BIND_NOT_VISIBLE) != 0) { - sb.append("NOTVIS "); + sb.append("!VIS "); + } + if ((flags&Context.BIND_VISIBLE) != 0) { + sb.append("VIS "); } if (serviceDead) { sb.append("DEAD "); diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java index de306b507ac3..8fb6a93c5ed9 100644 --- a/services/java/com/android/server/am/ContentProviderRecord.java +++ b/services/java/com/android/server/am/ContentProviderRecord.java @@ -25,6 +25,7 @@ import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Slog; import java.io.PrintWriter; @@ -199,6 +200,8 @@ class ContentProviderRecord { StringBuilder sb = new StringBuilder(128); sb.append("ContentProviderRecord{"); sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" u"); + sb.append(UserHandle.getUserId(uid)); sb.append(' '); sb.append(name.flattenToShortString()); sb.append('}'); diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags index a579f440c4b1..6ee750710d6c 100644 --- a/services/java/com/android/server/am/EventLogTags.logtags +++ b/services/java/com/android/server/am/EventLogTags.logtags @@ -14,72 +14,72 @@ option java_package com.android.server.am # google3/googledata/wireless/android/provisioning/gservices.config !! # # An activity is being finished: -30001 am_finish_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3) +30001 am_finish_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3) # A task is being brought to the front of the screen: -30002 am_task_to_front (Task|1|5) +30002 am_task_to_front (User|1|5),(Task|1|5) # An existing activity is being given a new intent: -30003 am_new_intent (Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) +30003 am_new_intent (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) # A new task is being created: -30004 am_create_task (Task ID|1|5) +30004 am_create_task (User|1|5),(Task ID|1|5) # A new activity is being created in an existing task: -30005 am_create_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) +30005 am_create_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) # An activity has been resumed into the foreground but was not already running: -30006 am_restart_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +30006 am_restart_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3) # An activity has been resumed and is now in the foreground: -30007 am_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +30007 am_resume_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3) # Application Not Responding -30008 am_anr (pid|1|5),(Package Name|3),(Flags|1|5),(reason|3) +30008 am_anr (User|1|5),(pid|1|5),(Package Name|3),(Flags|1|5),(reason|3) # Activity launch time -30009 activity_launch_time (Token|1|5),(Component Name|3),(time|2|3) +30009 am_activity_launch_time (User|1|5),(Token|1|5),(Component Name|3),(time|2|3) # Application process bound to work -30010 am_proc_bound (PID|1|5),(Process Name|3) +30010 am_proc_bound (User|1|5),(PID|1|5),(Process Name|3) # Application process died -30011 am_proc_died (PID|1|5),(Process Name|3) +30011 am_proc_died (User|1|5),(PID|1|5),(Process Name|3) # The Activity Manager failed to pause the given activity. -30012 am_failed_to_pause (Token|1|5),(Wanting to pause|3),(Currently pausing|3) +30012 am_failed_to_pause (User|1|5),(Token|1|5),(Wanting to pause|3),(Currently pausing|3) # Attempting to pause the current activity -30013 am_pause_activity (Token|1|5),(Component Name|3) +30013 am_pause_activity (User|1|5),(Token|1|5),(Component Name|3) # Application process has been started -30014 am_proc_start (PID|1|5),(UID|1|5),(Process Name|3),(Type|3),(Component|3) +30014 am_proc_start (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3),(Type|3),(Component|3) # An application process has been marked as bad -30015 am_proc_bad (UID|1|5),(Process Name|3) +30015 am_proc_bad (User|1|5),(UID|1|5),(Process Name|3) # An application process that was bad is now marked as good -30016 am_proc_good (UID|1|5),(Process Name|3) +30016 am_proc_good (User|1|5),(UID|1|5),(Process Name|3) # Reporting to applications that memory is low 30017 am_low_memory (Num Processes|1|1) # An activity is being destroyed: -30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3) +30018 am_destroy_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3) # An activity has been relaunched, resumed, and is now in the foreground: -30019 am_relaunch_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +30019 am_relaunch_resume_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3) # An activity has been relaunched: -30020 am_relaunch_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +30020 am_relaunch_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3) # The activity's onPause has been called. -30021 am_on_paused_called (Component Name|3) +30021 am_on_paused_called (User|1|5),(Component Name|3) # The activity's onResume has been called. -30022 am_on_resume_called (Component Name|3) +30022 am_on_resume_called (User|1|5),(Component Name|3) # Kill a process to reclaim memory. -30023 am_kill (PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3) +30023 am_kill (User|1|5),(PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3) # Discard an undelivered serialized broadcast (timeout/ANR/crash) -30024 am_broadcast_discard_filter (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5) -30025 am_broadcast_discard_app (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3) +30024 am_broadcast_discard_filter (User|1|5),(Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5) +30025 am_broadcast_discard_app (User|1|5),(Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3) # A service is being created -30030 am_create_service (Service Record|1|5),(Name|3),(Intent|3),(PID|1|5) +30030 am_create_service (User|1|5),(Service Record|1|5),(Name|3),(Intent|3),(PID|1|5) # A service is being destroyed -30031 am_destroy_service (Service Record|1|5),(Name|3),(PID|1|5) +30031 am_destroy_service (User|1|5),(Service Record|1|5),(Name|3),(PID|1|5) # A process has crashed too many times, it is being cleared -30032 am_process_crashed_too_much (Name|3),(PID|1|5) +30032 am_process_crashed_too_much (User|1|5),(Name|3),(PID|1|5) # An unknown process is trying to attach to the activity manager 30033 am_drop_process (PID|1|5) # A service has crashed too many times, it is being stopped -30034 am_service_crashed_too_much (Crash Count|1|1),(Component Name|3),(PID|1|5) +30034 am_service_crashed_too_much (User|1|5),(Crash Count|1|1),(Component Name|3),(PID|1|5) # A service is going to be restarted after its process went away -30035 am_schedule_service_restart (Component Name|3),(Time|2|3) +30035 am_schedule_service_restart (User|1|5),(Component Name|3),(Time|2|3) # A client was waiting for a content provider, but its process was lost -30036 am_provider_lost_process (Package Name|3),(UID|1|5),(Name|3) +30036 am_provider_lost_process (User|1|5),(Package Name|3),(UID|1|5),(Name|3) # The activity manager gave up on a new process taking too long to start -30037 am_process_start_timeout (PID|1|5),(UID|1|5),(Process Name|3) +30037 am_process_start_timeout (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3) # Unhandled exception -30039 am_crash (PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5) +30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5) # Log.wtf() called -30040 am_wtf (PID|1|5),(Process Name|3),(Flags|1|5),(Tag|3),(Message|3) +30040 am_wtf (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Tag|3),(Message|3) diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index afc060ecc6b7..9e25e3024513 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -101,7 +101,24 @@ class ProcessList { // The maximum number of hidden processes we will keep around before // killing them; this is just a control to not let us go too crazy with // keeping around processes on devices with large amounts of RAM. - static final int MAX_HIDDEN_APPS = 15; + static final int MAX_HIDDEN_APPS = 24; + + // We allow empty processes to stick around for at most 30 minutes. + static final long MAX_EMPTY_TIME = 30*60*1000; + + // The number of hidden at which we don't consider it necessary to do + // memory trimming. + static final int TRIM_HIDDEN_APPS = 3; + + // The number of empty apps at which we don't consider it necessary to do + // memory trimming. + static final int TRIM_EMPTY_APPS = 3; + + // Threshold of number of hidden+empty where we consider memory critical. + static final int TRIM_CRITICAL_THRESHOLD = 3; + + // Threshold of number of hidden+empty where we consider memory critical. + static final int TRIM_LOW_THRESHOLD = 5; // We put empty content processes after any hidden processes that have // been idle for less than 15 seconds. diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index d37242259e08..652fdb58b45d 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -61,6 +61,7 @@ class ProcessRecord { long lruWeight; // Weight for ordering in LRU list int maxAdj; // Maximum OOM adjustment for this process int hiddenAdj; // If hidden, this is the adjustment to use + int clientHiddenAdj; // If empty but hidden client, this is the adjustment to use int emptyAdj; // If empty, this is the adjustment to use int curRawAdj; // Current OOM unlimited adjustment for this process int setRawAdj; // Last set OOM unlimited adjustment for this process @@ -75,6 +76,7 @@ class ProcessRecord { boolean keeping; // Actively running code so don't kill due to that? boolean setIsForeground; // Running foreground UI when last set? boolean hasActivities; // Are there any activities running in this process? + boolean hasClientActivities; // Are there any client services with activities? boolean foregroundServices; // Running any services that are foreground? boolean foregroundActivities; // Running any activities that are foreground? boolean systemNoUi; // This is a system process, but not currently showing UI. @@ -188,8 +190,7 @@ class ProcessRecord { instrumentationInfo.dump(new PrintWriterPrinter(pw), prefix + " "); } } - pw.print(prefix); pw.print("thread="); pw.print(thread); - pw.print(" curReceiver="); pw.println(curReceiver); + pw.print(prefix); pw.print("thread="); pw.println(thread); pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting="); pw.print(starting); pw.print(" lastPss="); pw.println(lastPss); pw.print(prefix); pw.print("lastActivityTime="); @@ -201,6 +202,7 @@ class ProcessRecord { pw.print(" empty="); pw.println(empty); pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj); pw.print(" hidden="); pw.print(hiddenAdj); + pw.print(" client="); pw.print(clientHiddenAdj); pw.print(" empty="); pw.print(emptyAdj); pw.print(" curRaw="); pw.print(curRawAdj); pw.print(" setRaw="); pw.print(setRawAdj); @@ -211,18 +213,27 @@ class ProcessRecord { pw.print(" setSchedGroup="); pw.print(setSchedGroup); pw.print(" systemNoUi="); pw.print(systemNoUi); pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel); - pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi); - pw.print(" pendingUiClean="); pw.print(pendingUiClean); - pw.print(" hasAboveClient="); pw.println(hasAboveClient); - pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground); - pw.print(" foregroundServices="); pw.print(foregroundServices); - pw.print(" forcingToForeground="); pw.println(forcingToForeground); - pw.print(prefix); pw.print("persistent="); pw.print(persistent); - pw.print(" removed="); pw.print(removed); - pw.print(" hasActivities="); pw.print(hasActivities); - pw.print(" foregroundActivities="); pw.println(foregroundActivities); pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq); pw.print(" lruSeq="); pw.println(lruSeq); + if (hasShownUi || pendingUiClean || hasAboveClient) { + pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi); + pw.print(" pendingUiClean="); pw.print(pendingUiClean); + pw.print(" hasAboveClient="); pw.println(hasAboveClient); + } + if (setIsForeground || foregroundServices || forcingToForeground != null) { + pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground); + pw.print(" foregroundServices="); pw.print(foregroundServices); + pw.print(" forcingToForeground="); pw.println(forcingToForeground); + } + if (persistent || removed) { + pw.print(prefix); pw.print("persistent="); pw.print(persistent); + pw.print(" removed="); pw.println(removed); + } + if (hasActivities || hasClientActivities || foregroundActivities) { + pw.print(prefix); pw.print("hasActivities="); pw.print(hasActivities); + pw.print(" hasClientActivities="); pw.print(hasClientActivities); + pw.print(" foregroundActivities="); pw.println(foregroundActivities); + } if (!keeping) { long wtime; synchronized (batteryStats.getBatteryStats()) { @@ -231,10 +242,10 @@ class ProcessRecord { } long timeUsed = wtime - lastWakeTime; pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime); - pw.print(" time used="); + pw.print(" timeUsed="); TimeUtils.formatDuration(timeUsed, pw); pw.println(""); pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime); - pw.print(" time used="); + pw.print(" timeUsed="); TimeUtils.formatDuration(curCpuTime-lastCpuTime, pw); pw.println(""); } pw.print(prefix); pw.print("lastRequestedGc="); @@ -299,6 +310,9 @@ class ProcessRecord { pw.print(prefix); pw.print(" - "); pw.println(conProviders.get(i).toShortString()); } } + if (curReceiver != null) { + pw.print(prefix); pw.print("curReceiver="); pw.println(curReceiver); + } if (receivers.size() > 0) { pw.print(prefix); pw.println("Receivers:"); for (ReceiverList rl : receivers) { @@ -318,7 +332,7 @@ class ProcessRecord { pkgList.add(_info.packageName); thread = _thread; maxAdj = ProcessList.HIDDEN_APP_MAX_ADJ; - hiddenAdj = emptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ; + hiddenAdj = clientHiddenAdj = emptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ; curRawAdj = setRawAdj = -100; curAdj = setAdj = -100; persistent = false; diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 7055fdc66386..84e824af9dea 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -425,6 +425,7 @@ class ServiceRecord extends Binder { StringBuilder sb = new StringBuilder(128); sb.append("ServiceRecord{") .append(Integer.toHexString(System.identityHashCode(this))) + .append(" u").append(userId) .append(' ').append(shortName).append('}'); return stringName = sb.toString(); } diff --git a/services/java/com/android/server/dreams/DreamController.java b/services/java/com/android/server/dreams/DreamController.java new file mode 100644 index 000000000000..81c801871374 --- /dev/null +++ b/services/java/com/android/server/dreams/DreamController.java @@ -0,0 +1,243 @@ +/* + * 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.dreams; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.IBinder.DeathRecipient; +import android.service.dreams.Dream; +import android.service.dreams.IDreamService; +import android.util.Slog; +import android.view.IWindowManager; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import java.io.PrintWriter; +import java.util.NoSuchElementException; + +/** + * Internal controller for starting and stopping the current dream and managing related state. + * + * Assumes all operations are called from the dream handler thread. + */ +final class DreamController { + private static final String TAG = "DreamController"; + + private final Context mContext; + private final Handler mHandler; + private final Listener mListener; + private final IWindowManager mIWindowManager; + + private final Intent mDreamingStartedIntent = new Intent(Dream.ACTION_DREAMING_STARTED) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + private final Intent mDreamingStoppedIntent = new Intent(Dream.ACTION_DREAMING_STOPPED) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + + private DreamRecord mCurrentDream; + + public DreamController(Context context, Handler handler, Listener listener) { + mContext = context; + mHandler = handler; + mListener = listener; + mIWindowManager = WindowManagerGlobal.getWindowManagerService(); + } + + public void dump(PrintWriter pw) { + pw.println("Dreamland:"); + if (mCurrentDream != null) { + pw.println(" mCurrentDream:"); + pw.println(" mToken=" + mCurrentDream.mToken); + pw.println(" mName=" + mCurrentDream.mName); + pw.println(" mIsTest=" + mCurrentDream.mIsTest); + pw.println(" mUserId=" + mCurrentDream.mUserId); + pw.println(" mBound=" + mCurrentDream.mBound); + pw.println(" mService=" + mCurrentDream.mService); + pw.println(" mSentStartBroadcast=" + mCurrentDream.mSentStartBroadcast); + } else { + pw.println(" mCurrentDream: null"); + } + } + + public void startDream(Binder token, ComponentName name, boolean isTest, int userId) { + stopDream(); + + Slog.i(TAG, "Starting dream: name=" + name + ", isTest=" + isTest + ", userId=" + userId); + + mCurrentDream = new DreamRecord(token, name, isTest, userId); + + try { + mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM); + } catch (RemoteException ex) { + Slog.e(TAG, "Unable to add window token for dream.", ex); + stopDream(); + return; + } + + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Dream.CATEGORY_DREAM); + intent.setComponent(name); + intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + try { + if (!mContext.bindService(intent, mCurrentDream, + Context.BIND_AUTO_CREATE, userId)) { + Slog.e(TAG, "Unable to bind dream service: " + intent); + stopDream(); + return; + } + } catch (SecurityException ex) { + Slog.e(TAG, "Unable to bind dream service: " + intent, ex); + stopDream(); + return; + } + + mCurrentDream.mBound = true; + } + + public void stopDream() { + if (mCurrentDream == null) { + return; + } + + final DreamRecord oldDream = mCurrentDream; + mCurrentDream = null; + Slog.i(TAG, "Stopping dream: name=" + oldDream.mName + + ", isTest=" + oldDream.mIsTest + ", userId=" + oldDream.mUserId); + + if (oldDream.mSentStartBroadcast) { + mContext.sendBroadcast(mDreamingStoppedIntent); + } + + if (oldDream.mService != null) { + // TODO: It would be nice to tell the dream that it's being stopped so that + // it can shut down nicely before we yank its window token out from under it. + try { + oldDream.mService.asBinder().unlinkToDeath(oldDream, 0); + } catch (NoSuchElementException ex) { + // don't care + } + oldDream.mService = null; + } + + if (oldDream.mBound) { + mContext.unbindService(oldDream); + } + + try { + mIWindowManager.removeWindowToken(oldDream.mToken); + } catch (RemoteException ex) { + Slog.w(TAG, "Error removing window token for dream.", ex); + } + + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onDreamStopped(oldDream.mToken); + } + }); + } + + private void attach(IDreamService service) { + try { + service.asBinder().linkToDeath(mCurrentDream, 0); + service.attach(mCurrentDream.mToken); + } catch (RemoteException ex) { + Slog.e(TAG, "The dream service died unexpectedly.", ex); + stopDream(); + return; + } + + mCurrentDream.mService = service; + + if (!mCurrentDream.mIsTest) { + mContext.sendBroadcast(mDreamingStartedIntent); + mCurrentDream.mSentStartBroadcast = true; + } + } + + /** + * Callback interface to be implemented by the {@link DreamManagerService}. + */ + public interface Listener { + void onDreamStopped(Binder token); + } + + private final class DreamRecord implements DeathRecipient, ServiceConnection { + public final Binder mToken; + public final ComponentName mName; + public final boolean mIsTest; + public final int mUserId; + + public boolean mBound; + public IDreamService mService; + public boolean mSentStartBroadcast; + + public DreamRecord(Binder token, ComponentName name, + boolean isTest, int userId) { + mToken = token; + mName = name; + mIsTest = isTest; + mUserId = userId; + } + + // May be called on any thread. + @Override + public void binderDied() { + mHandler.post(new Runnable() { + @Override + public void run() { + mService = null; + if (mCurrentDream == DreamRecord.this) { + stopDream(); + } + } + }); + } + + // May be called on any thread. + @Override + public void onServiceConnected(ComponentName name, final IBinder service) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (mCurrentDream == DreamRecord.this && mService == null) { + attach(IDreamService.Stub.asInterface(service)); + } + } + }); + } + + // May be called on any thread. + @Override + public void onServiceDisconnected(ComponentName name) { + mHandler.post(new Runnable() { + @Override + public void run() { + mService = null; + if (mCurrentDream == DreamRecord.this) { + stopDream(); + } + } + }); + } + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/dreams/DreamManagerService.java b/services/java/com/android/server/dreams/DreamManagerService.java new file mode 100644 index 000000000000..1f4017614617 --- /dev/null +++ b/services/java/com/android/server/dreams/DreamManagerService.java @@ -0,0 +1,383 @@ +/* + * 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.dreams; + +import com.android.internal.util.DumpUtils; + +import android.app.ActivityManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.PowerManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.service.dreams.IDreamManager; +import android.util.Slog; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +import libcore.util.Objects; + +/** + * Service api for managing dreams. + * + * @hide + */ +public final class DreamManagerService extends IDreamManager.Stub { + private static final boolean DEBUG = true; + private static final String TAG = "DreamManagerService"; + + private final Object mLock = new Object(); + + private final Context mContext; + private final DreamHandler mHandler; + private final DreamController mController; + private final PowerManager mPowerManager; + + private Binder mCurrentDreamToken; + private ComponentName mCurrentDreamName; + private int mCurrentDreamUserId; + private boolean mCurrentDreamIsTest; + + public DreamManagerService(Context context, Handler mainHandler) { + mContext = context; + mHandler = new DreamHandler(mainHandler.getLooper()); + mController = new DreamController(context, mHandler, mControllerListener); + + mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + } + + public void systemReady() { + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + stopDreamLocked(); + } + } + }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + + pw.println("DREAM MANAGER (dumpsys dreams)"); + pw.println(); + + pw.println("mCurrentDreamToken=" + mCurrentDreamToken); + pw.println("mCurrentDreamName=" + mCurrentDreamName); + pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId); + pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest); + pw.println(); + + DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() { + @Override + public void dump(PrintWriter pw) { + mController.dump(pw); + } + }, pw, 200); + } + + @Override // Binder call + public ComponentName[] getDreamComponents() { + checkPermission(android.Manifest.permission.READ_DREAM_STATE); + + final int userId = UserHandle.getCallingUserId(); + final long ident = Binder.clearCallingIdentity(); + try { + return getDreamComponentsForUser(userId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public void setDreamComponents(ComponentName[] componentNames) { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + + final int userId = UserHandle.getCallingUserId(); + final long ident = Binder.clearCallingIdentity(); + try { + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.SCREENSAVER_COMPONENTS, + componentsToString(componentNames), + userId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public ComponentName getDefaultDreamComponent() { + checkPermission(android.Manifest.permission.READ_DREAM_STATE); + + final int userId = UserHandle.getCallingUserId(); + final long ident = Binder.clearCallingIdentity(); + try { + String name = Settings.Secure.getStringForUser(mContext.getContentResolver(), + Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT, + userId); + return name == null ? null : ComponentName.unflattenFromString(name); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public boolean isDreaming() { + checkPermission(android.Manifest.permission.READ_DREAM_STATE); + + synchronized (mLock) { + return mCurrentDreamToken != null && !mCurrentDreamIsTest; + } + } + + @Override // Binder call + public void dream() { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + + final long ident = Binder.clearCallingIdentity(); + try { + // Ask the power manager to nap. It will eventually call back into + // startDream() if/when it is appropriate to start dreaming. + // Because napping could cause the screen to turn off immediately if the dream + // cannot be started, we keep one eye open and gently poke user activity. + long time = SystemClock.uptimeMillis(); + mPowerManager.userActivity(time, true /*noChangeLights*/); + mPowerManager.nap(time); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public void testDream(ComponentName dream) { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + + if (dream == null) { + throw new IllegalArgumentException("dream must not be null"); + } + + final int callingUserId = UserHandle.getCallingUserId(); + final int currentUserId = ActivityManager.getCurrentUser(); + if (callingUserId != currentUserId) { + // This check is inherently prone to races but at least it's something. + Slog.w(TAG, "Aborted attempt to start a test dream while a different " + + " user is active: callingUserId=" + callingUserId + + ", currentUserId=" + currentUserId); + return; + } + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + startDreamLocked(dream, true /*isTest*/, callingUserId); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public void awaken() { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + + final long ident = Binder.clearCallingIdentity(); + try { + // Treat an explicit request to awaken as user activity so that the + // device doesn't immediately go to sleep if the timeout expired, + // for example when being undocked. + long time = SystemClock.uptimeMillis(); + mPowerManager.userActivity(time, false /*noChangeLights*/); + stopDream(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public void finishSelf(IBinder token) { + // Requires no permission, called by Dream from an arbitrary process. + if (token == null) { + throw new IllegalArgumentException("token must not be null"); + } + + final long ident = Binder.clearCallingIdentity(); + try { + if (DEBUG) { + Slog.d(TAG, "Dream finished: " + token); + } + + // Note that a dream finishing and self-terminating is not + // itself considered user activity. If the dream is ending because + // the user interacted with the device then user activity will already + // have been poked so the device will stay awake a bit longer. + // If the dream is ending on its own for other reasons and no wake + // locks are held and the user activity timeout has expired then the + // device may simply go to sleep. + synchronized (mLock) { + if (mCurrentDreamToken == token) { + stopDreamLocked(); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** + * Called by the power manager to start a dream. + */ + public void startDream() { + int userId = ActivityManager.getCurrentUser(); + ComponentName dream = chooseDreamForUser(userId); + if (dream != null) { + synchronized (mLock) { + startDreamLocked(dream, false /*isTest*/, userId); + } + } + } + + /** + * Called by the power manager to stop a dream. + */ + public void stopDream() { + synchronized (mLock) { + stopDreamLocked(); + } + } + + private ComponentName chooseDreamForUser(int userId) { + ComponentName[] dreams = getDreamComponentsForUser(userId); + return dreams != null && dreams.length != 0 ? dreams[0] : null; + } + + private ComponentName[] getDreamComponentsForUser(int userId) { + String names = Settings.Secure.getStringForUser(mContext.getContentResolver(), + Settings.Secure.SCREENSAVER_COMPONENTS, + userId); + return names == null ? null : componentsFromString(names); + } + + private void startDreamLocked(final ComponentName name, + final boolean isTest, final int userId) { + if (Objects.equal(mCurrentDreamName, name) + && mCurrentDreamIsTest == isTest + && mCurrentDreamUserId == userId) { + return; + } + + stopDreamLocked(); + + Slog.i(TAG, "Entering dreamland."); + + final Binder newToken = new Binder(); + mCurrentDreamToken = newToken; + mCurrentDreamName = name; + mCurrentDreamIsTest = isTest; + mCurrentDreamUserId = userId; + + mHandler.post(new Runnable() { + @Override + public void run() { + mController.startDream(newToken, name, isTest, userId); + } + }); + } + + private void stopDreamLocked() { + if (mCurrentDreamToken != null) { + Slog.i(TAG, "Leaving dreamland."); + + cleanupDreamLocked(); + + mHandler.post(new Runnable() { + @Override + public void run() { + mController.stopDream(); + } + }); + } + } + + private void cleanupDreamLocked() { + mCurrentDreamToken = null; + mCurrentDreamName = null; + mCurrentDreamIsTest = false; + mCurrentDreamUserId = 0; + } + + private void checkPermission(String permission) { + if (mContext.checkCallingOrSelfPermission(permission) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Access denied to process: " + Binder.getCallingPid() + + ", must have permission " + permission); + } + } + + private static String componentsToString(ComponentName[] componentNames) { + StringBuilder names = new StringBuilder(); + if (componentNames != null) { + for (ComponentName componentName : componentNames) { + if (names.length() > 0) { + names.append(','); + } + names.append(componentName.flattenToString()); + } + } + return names.toString(); + } + + private static ComponentName[] componentsFromString(String names) { + String[] namesArray = names.split(","); + ComponentName[] componentNames = new ComponentName[namesArray.length]; + for (int i = 0; i < namesArray.length; i++) { + componentNames[i] = ComponentName.unflattenFromString(namesArray[i]); + } + return componentNames; + } + + private final DreamController.Listener mControllerListener = new DreamController.Listener() { + @Override + public void onDreamStopped(Binder token) { + synchronized (mLock) { + if (mCurrentDreamToken == token) { + cleanupDreamLocked(); + } + } + } + }; + + /** + * Handler for asynchronous operations performed by the dream manager. + * Ensures operations to {@link DreamController} are single-threaded. + */ + private final class DreamHandler extends Handler { + public DreamHandler(Looper looper) { + super(looper, null, true /*async*/); + } + } +} diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index 030eb5eae65d..ad138e80140b 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -24,6 +24,7 @@ import com.android.server.TwilightService; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService; import com.android.server.display.DisplayManagerService; +import com.android.server.dreams.DreamManagerService; import android.Manifest; import android.content.BroadcastReceiver; @@ -46,13 +47,11 @@ import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; import android.service.dreams.Dream; -import android.service.dreams.IDreamManager; import android.util.EventLog; import android.util.Log; import android.util.Slog; @@ -100,14 +99,12 @@ public final class PowerManagerService extends IPowerManager.Stub private static final int DIRTY_STAY_ON = 1 << 7; // Dirty bit: battery state changed private static final int DIRTY_BATTERY_STATE = 1 << 8; - // Dirty bit: dream ended - private static final int DIRTY_DREAM_ENDED = 1 << 9; // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp(). // The screen should be off or in the process of being turned off by the display controller. private static final int WAKEFULNESS_ASLEEP = 0; // Wakefulness: The device is fully awake. It can be put to sleep by a call to goToSleep(). - // When the user activity timeout expires, the device may start napping. + // When the user activity timeout expires, the device may start napping or go to sleep. private static final int WAKEFULNESS_AWAKE = 1; // Wakefulness: The device is napping. It is deciding whether to dream or go to sleep // but hasn't gotten around to it yet. It can be awoken by a call to wakeUp(), which @@ -149,7 +146,7 @@ public final class PowerManagerService extends IPowerManager.Stub private Notifier mNotifier; private DisplayPowerController mDisplayPowerController; private SettingsObserver mSettingsObserver; - private IDreamManager mDreamManager; + private DreamManagerService mDreamManager; private LightsService.Light mAttentionLight; private final Object mLock = new Object(); @@ -335,9 +332,10 @@ public final class PowerManagerService extends IPowerManager.Stub } } - public void systemReady(TwilightService twilight) { + public void systemReady(TwilightService twilight, DreamManagerService dreamManager) { synchronized (mLock) { mSystemReady = true; + mDreamManager = dreamManager; PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting(); @@ -365,10 +363,7 @@ public final class PowerManagerService extends IPowerManager.Stub mContext.registerReceiver(new BootCompletedReceiver(), filter, null, mHandler); filter = new IntentFilter(); - filter.addAction(Intent.ACTION_DOCK_EVENT); - mContext.registerReceiver(new DockReceiver(), filter, null, mHandler); - - filter = new IntentFilter(); + filter.addAction(Dream.ACTION_DREAMING_STARTED); filter.addAction(Dream.ACTION_DREAMING_STOPPED); mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler); @@ -887,6 +882,47 @@ public final class PowerManagerService extends IPowerManager.Stub return true; } + @Override // Binder call + public void nap(long eventTime) { + if (eventTime > SystemClock.uptimeMillis()) { + throw new IllegalArgumentException("event time must not be in the future"); + } + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + napInternal(eventTime); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void napInternal(long eventTime) { + synchronized (mLock) { + if (napNoUpdateLocked(eventTime)) { + updatePowerStateLocked(); + } + } + } + + private boolean napNoUpdateLocked(long eventTime) { + if (DEBUG_SPEW) { + Slog.d(TAG, "napNoUpdateLocked: eventTime=" + eventTime); + } + + if (eventTime < mLastWakeTime || mWakefulness != WAKEFULNESS_AWAKE + || !mBootCompleted || !mSystemReady) { + return false; + } + + Slog.i(TAG, "Nap time..."); + + mDirty |= DIRTY_WAKEFULNESS; + mWakefulness = WAKEFULNESS_NAPPING; + return true; + } + /** * Updates the global power state based on dirty bits recorded in mDirty. * @@ -1143,11 +1179,15 @@ public final class PowerManagerService extends IPowerManager.Stub | DIRTY_WAKEFULNESS | DIRTY_STAY_ON)) != 0) { if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) { if (DEBUG_SPEW) { - Slog.d(TAG, "updateWakefulnessLocked: Nap time..."); + Slog.d(TAG, "updateWakefulnessLocked: Bed time..."); + } + final long time = SystemClock.uptimeMillis(); + if (mDreamsActivateOnSleepSetting) { + changed = napNoUpdateLocked(time); + } else { + changed = goToSleepNoUpdateLocked(time, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); } - mWakefulness = WAKEFULNESS_NAPPING; - mDirty |= DIRTY_WAKEFULNESS; - changed = true; } } return changed; @@ -1172,8 +1212,7 @@ public final class PowerManagerService extends IPowerManager.Stub | DIRTY_SETTINGS | DIRTY_IS_POWERED | DIRTY_STAY_ON - | DIRTY_BATTERY_STATE - | DIRTY_DREAM_ENDED)) != 0) { + | DIRTY_BATTERY_STATE)) != 0) { scheduleSandmanLocked(); } } @@ -1210,32 +1249,15 @@ public final class PowerManagerService extends IPowerManager.Stub } } - // Get the dream manager, if needed. - if (startDreaming && mDreamManager == null) { - mDreamManager = IDreamManager.Stub.asInterface( - ServiceManager.checkService("dreams")); - if (mDreamManager == null) { - Slog.w(TAG, "Unable to find IDreamManager."); - } - } - // Start dreaming if needed. // We only control the dream on the handler thread, so we don't need to worry about // concurrent attempts to start or stop the dream. boolean isDreaming = false; if (mDreamManager != null) { - try { - isDreaming = mDreamManager.isDreaming(); - if (startDreaming && !isDreaming) { - Slog.i(TAG, "Entering dreamland."); - mDreamManager.dream(); - isDreaming = mDreamManager.isDreaming(); - if (!isDreaming) { - Slog.i(TAG, "Could not enter dreamland. Sleep will be dreamless."); - } - } - } catch (RemoteException ex) { + if (startDreaming) { + mDreamManager.startDream(); } + isDreaming = mDreamManager.isDreaming(); } // Update dream state. @@ -1255,18 +1277,6 @@ public final class PowerManagerService extends IPowerManager.Stub if (!continueDreaming) { handleDreamFinishedLocked(); } - - // In addition to listening for the intent, poll the sandman periodically to detect - // when the dream has ended (as a watchdog only, ensuring our state is always correct). - if (mWakefulness == WAKEFULNESS_DREAMING - || mWakefulness == WAKEFULNESS_NAPPING) { - if (!mSandmanScheduled) { - mSandmanScheduled = true; - Message msg = mHandler.obtainMessage(MSG_SANDMAN); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, 5000); - } - } } // Stop dreaming if needed. @@ -1274,26 +1284,22 @@ public final class PowerManagerService extends IPowerManager.Stub // If so, then the power manager will have posted another message to the handler // to take care of it later. if (mDreamManager != null) { - try { - if (!continueDreaming && isDreaming) { - Slog.i(TAG, "Leaving dreamland."); - mDreamManager.awaken(); - } - } catch (RemoteException ex) { + if (!continueDreaming) { + mDreamManager.stopDream(); } } } /** * Returns true if the device is allowed to dream in its current state, - * assuming there has been no recent user activity and no wake locks are held. + * assuming that there was either an explicit request to nap or the user activity + * timeout expired and no wake locks are held. */ private boolean canDreamLocked() { return mIsPowered && mDreamsSupportedConfig && mDreamsEnabledSetting - && mDreamsActivateOnSleepSetting - && !mBatteryService.isBatteryLow(); + && mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF; } /** @@ -1313,7 +1319,6 @@ public final class PowerManagerService extends IPowerManager.Stub } } - /** * Updates the display power state asynchronously. * When the update is finished, mDisplayReady will be set to true. The display @@ -1494,15 +1499,6 @@ public final class PowerManagerService extends IPowerManager.Stub updatePowerStateLocked(); } - private void handleDockStateChangedLocked(int dockState) { - // TODO - } - - private void handleDreamEndedLocked() { - mDirty |= DIRTY_DREAM_ENDED; - updatePowerStateLocked(); - } - /** * Reboot the device immediately, passing 'reason' (may be null) * to the underlying __reboot system call. Should not return. @@ -1957,22 +1953,11 @@ public final class PowerManagerService extends IPowerManager.Stub } } - private final class DockReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - synchronized (mLock) { - int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, - Intent.EXTRA_DOCK_STATE_UNDOCKED); - handleDockStateChangedLocked(dockState); - } - } - } - private final class DreamReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { synchronized (mLock) { - handleDreamEndedLocked(); + scheduleSandmanLocked(); } } } diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml index 9dfe4a1f99c7..15d075cf5502 100644 --- a/tests/ActivityTests/AndroidManifest.xml +++ b/tests/ActivityTests/AndroidManifest.xml @@ -21,6 +21,8 @@ <uses-permission android:name="android.permission.REMOVE_TASKS" /> <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> <application android:label="ActivityTest"> <activity android:name="ActivityTestMain"> <intent-filter> @@ -31,6 +33,8 @@ <service android:name="SingleUserService" android:singleUser="true" android:exported="true"> </service> + <service android:name="ServiceUserTarget"> + </service> <receiver android:name="UserTarget"> </receiver> <receiver android:name="SingleUserReceiver" diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index 2348e99e46eb..f0c3b2246ffb 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -16,6 +16,7 @@ package com.google.android.test.activity; +import java.util.ArrayList; import java.util.List; import android.app.Activity; @@ -31,6 +32,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.graphics.Bitmap; import android.widget.ImageView; import android.widget.LinearLayout; @@ -41,6 +43,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.content.Context; +import android.content.pm.UserInfo; import android.content.res.Configuration; import android.util.Log; @@ -51,6 +54,9 @@ public class ActivityTestMain extends Activity { ActivityManager mAm; Configuration mOverrideConfig; + int mSecondUser; + + ArrayList<ServiceConnection> mConnections = new ArrayList<ServiceConnection>(); class BroadcastResultReceiver extends BroadcastReceiver { @Override @@ -122,6 +128,15 @@ public class ActivityTestMain extends Activity { applyOverrideConfiguration(mOverrideConfig); } } + + UserManager um = (UserManager)getSystemService(Context.USER_SERVICE); + List<UserInfo> users = um.getUsers(); + mSecondUser = Integer.MAX_VALUE; + for (UserInfo ui : users) { + if (ui.id != 0 && mSecondUser > ui.id) { + mSecondUser = ui.id; + } + } } @Override @@ -148,7 +163,12 @@ public class ActivityTestMain extends Activity { Log.i(TAG, "Service disconnected " + name); } }; - bindService(intent, conn, Context.BIND_AUTO_CREATE); + if (bindService(intent, conn, Context.BIND_AUTO_CREATE)) { + mConnections.add(conn); + } else { + Toast.makeText(ActivityTestMain.this, "Failed to bind", + Toast.LENGTH_LONG).show(); + } return true; } }); @@ -185,15 +205,70 @@ public class ActivityTestMain extends Activity { return true; } }); - menu.add("Send to user 1!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + menu.add("Send to user 0!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + Intent intent = new Intent(ActivityTestMain.this, UserTarget.class); + sendOrderedBroadcastAsUser(intent, new UserHandle(0), null, + new BroadcastResultReceiver(), + null, Activity.RESULT_OK, null, null); + return true; + } + }); + menu.add("Send to user " + mSecondUser + "!").setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { Intent intent = new Intent(ActivityTestMain.this, UserTarget.class); - sendOrderedBroadcastAsUser(intent, new UserHandle(1), null, + sendOrderedBroadcastAsUser(intent, new UserHandle(mSecondUser), null, new BroadcastResultReceiver(), null, Activity.RESULT_OK, null, null); return true; } }); + menu.add("Bind to user 0!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + Intent intent = new Intent(ActivityTestMain.this, ServiceUserTarget.class); + ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + Log.i(TAG, "Service connected " + name + " " + service); + } + @Override + public void onServiceDisconnected(ComponentName name) { + Log.i(TAG, "Service disconnected " + name); + } + }; + if (bindService(intent, conn, Context.BIND_AUTO_CREATE, 0)) { + mConnections.add(conn); + } else { + Toast.makeText(ActivityTestMain.this, "Failed to bind", + Toast.LENGTH_LONG).show(); + } + return true; + } + }); + menu.add("Bind to user " + mSecondUser + "!").setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + Intent intent = new Intent(ActivityTestMain.this, ServiceUserTarget.class); + ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + Log.i(TAG, "Service connected " + name + " " + service); + } + @Override + public void onServiceDisconnected(ComponentName name) { + Log.i(TAG, "Service disconnected " + name); + } + }; + if (bindService(intent, conn, Context.BIND_AUTO_CREATE, mSecondUser)) { + mConnections.add(conn); + } else { + Toast.makeText(ActivityTestMain.this, "Failed to bind", + Toast.LENGTH_LONG).show(); + } + return true; + } + }); menu.add("Density!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { if (mOverrideConfig == null) { @@ -226,6 +301,15 @@ public class ActivityTestMain extends Activity { } } + @Override + protected void onStop() { + super.onStop(); + for (ServiceConnection conn : mConnections) { + unbindService(conn); + } + mConnections.clear(); + } + private View scrollWrap(View view) { ScrollView scroller = new ScrollView(this); scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ServiceUserTarget.java b/tests/ActivityTests/src/com/google/android/test/activity/ServiceUserTarget.java new file mode 100644 index 000000000000..a7474ec9b541 --- /dev/null +++ b/tests/ActivityTests/src/com/google/android/test/activity/ServiceUserTarget.java @@ -0,0 +1,41 @@ +/* + * 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.google.android.test.activity; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.os.UserHandle; +import android.widget.Toast; + +public class ServiceUserTarget extends Service { + Binder mBinder = new Binder(); + + @Override + public void onCreate() { + super.onCreate(); + Toast.makeText(this, + "Service created as user " + UserHandle.myUserId(), + Toast.LENGTH_LONG).show(); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } +} diff --git a/tests/ActivityTests/src/com/google/android/test/activity/SingleUserService.java b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserService.java index c40582a9c8ce..e9c340fbccd0 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/SingleUserService.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserService.java @@ -20,11 +20,21 @@ import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; +import android.os.UserHandle; +import android.widget.Toast; public class SingleUserService extends Service { Binder mBinder = new Binder(); @Override + public void onCreate() { + super.onCreate(); + Toast.makeText(this, + "Service created as user " + UserHandle.myUserId(), + Toast.LENGTH_LONG).show(); + } + + @Override public IBinder onBind(Intent intent) { return mBinder; } diff --git a/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml b/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml index 4715d6ef04b5..f0a2b9244a65 100644 --- a/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml +++ b/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml @@ -54,6 +54,10 @@ android:id="@+id/filterselection" android:layout_width="fill_parent" android:layout_height="wrap_content"/> + <Spinner + android:id="@+id/spinner1" + android:layout_width="fill_parent" + android:layout_height="wrap_content"/> <TextView android:id="@+id/slider1Text" android:layout_width="match_parent" diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blend.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blend.java new file mode 100644 index 000000000000..292082424543 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blend.java @@ -0,0 +1,176 @@ +/* + * 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.rs.image; + +import java.lang.Math; +import java.lang.Short; + +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.Matrix4f; +import android.renderscript.RenderScript; +import android.renderscript.Script; +import android.renderscript.ScriptC; +import android.renderscript.ScriptGroup; +import android.renderscript.ScriptIntrinsicBlend; +import android.renderscript.Type; +import android.util.Log; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.view.View; +import android.widget.Spinner; + +public class Blend extends TestBase { + private ScriptIntrinsicBlend mBlend; + private ScriptC_blend mBlendHelper; + private short image1Alpha = 128; + private short image2Alpha = 128; + + String mIntrinsicNames[]; + + private Allocation image1; + private Allocation image2; + private int currentIntrinsic = 0; + + private AdapterView.OnItemSelectedListener mIntrinsicSpinnerListener = + new AdapterView.OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + currentIntrinsic = pos; + runTest(); + act.updateDisplay(); + } + + public void onNothingSelected(AdapterView parent) { + + } + }; + + public void createTest(android.content.res.Resources res) { + mBlend = ScriptIntrinsicBlend.create(mRS, Element.U8_4(mRS)); + mBlendHelper = new ScriptC_blend(mRS); + mBlendHelper.set_alpha((short)128); + + image1 = Allocation.createTyped(mRS, mInPixelsAllocation.getType()); + image2 = Allocation.createTyped(mRS, mInPixelsAllocation2.getType()); + + mIntrinsicNames = new String[14]; + mIntrinsicNames[0] = "Source"; + mIntrinsicNames[1] = "Destination"; + mIntrinsicNames[2] = "Source Over"; + mIntrinsicNames[3] = "Destination Over"; + mIntrinsicNames[4] = "Source In"; + mIntrinsicNames[5] = "Destination In"; + mIntrinsicNames[6] = "Source Out"; + mIntrinsicNames[7] = "Destination Out"; + mIntrinsicNames[8] = "Source Atop"; + mIntrinsicNames[9] = "Destination Atop"; + mIntrinsicNames[10] = "XOR"; + mIntrinsicNames[11] = "Add"; + mIntrinsicNames[12] = "Subtract"; + mIntrinsicNames[13] = "Multiply"; + } + + public boolean onSpinner1Setup(Spinner s) { + s.setAdapter(new ArrayAdapter<String>( + act, R.layout.spinner_layout, mIntrinsicNames)); + s.setOnItemSelectedListener(mIntrinsicSpinnerListener); + return true; + } + + public boolean onBar1Setup(SeekBar b, TextView t) { + t.setText("Image 1 Alpha"); + b.setMax(255); + b.setProgress(image1Alpha); + return true; + } + + public void onBar1Changed(int progress) { + image1Alpha = (short)progress; + } + + public boolean onBar2Setup(SeekBar b, TextView t) { + t.setText("Image 2 Alpha"); + b.setMax(255); + b.setProgress(image2Alpha); + return true; + } + + public void onBar2Changed(int progress) { + image2Alpha = (short)progress; + } + + public void runTest() { + image1.copy2DRangeFrom(0, 0, mInPixelsAllocation.getType().getX(), mInPixelsAllocation.getType().getY(), mInPixelsAllocation, 0, 0); + image2.copy2DRangeFrom(0, 0, mInPixelsAllocation2.getType().getX(), mInPixelsAllocation2.getType().getY(), mInPixelsAllocation2, 0, 0); + + mBlendHelper.set_alpha(image1Alpha); + mBlendHelper.forEach_setImageAlpha(image1); + + mBlendHelper.set_alpha(image2Alpha); + mBlendHelper.forEach_setImageAlpha(image2); + + switch (currentIntrinsic) { + case 0: + mBlend.forEachSrc(image1, image2); + break; + case 1: + mBlend.forEachDst(image1, image2); + break; + case 2: + mBlend.forEachSrcOver(image1, image2); + break; + case 3: + mBlend.forEachDstOver(image1, image2); + break; + case 4: + mBlend.forEachSrcIn(image1, image2); + break; + case 5: + mBlend.forEachDstIn(image1, image2); + break; + case 6: + mBlend.forEachSrcOut(image1, image2); + break; + case 7: + mBlend.forEachDstOut(image1, image2); + break; + case 8: + mBlend.forEachSrcAtop(image1, image2); + break; + case 9: + mBlend.forEachDstAtop(image1, image2); + break; + case 10: + mBlend.forEachXor(image1, image2); + break; + case 11: + mBlend.forEachAdd(image1, image2); + break; + case 12: + mBlend.forEachSubtract(image1, image2); + break; + case 13: + mBlend.forEachMultiply(image1, image2); + break; + } + + mOutPixelsAllocation.copy2DRangeFrom(0, 0, image2.getType().getX(), image2.getType().getY(), image2, 0, 0); + } + +} diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java index a8462e6e6449..db0ef787a96e 100644 --- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java +++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java @@ -55,9 +55,11 @@ public class ImageProcessingActivity extends Activity private final String RESULT_FILE = "image_processing_result.csv"; Bitmap mBitmapIn; + Bitmap mBitmapIn2; Bitmap mBitmapOut; String mTestNames[]; + private Spinner mSpinner; private SeekBar mBar1; private SeekBar mBar2; private SeekBar mBar3; @@ -81,6 +83,10 @@ public class ImageProcessingActivity extends Activity private TestBase mTest; + public void updateDisplay() { + mTest.updateBitmap(mBitmapOut); + mDisplayView.invalidate(); + } public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { @@ -98,8 +104,7 @@ public class ImageProcessingActivity extends Activity } mTest.runTest(); - mTest.updateBitmap(mBitmapOut); - mDisplayView.invalidate(); + updateDisplay(); } } @@ -110,6 +115,9 @@ public class ImageProcessingActivity extends Activity } void setupBars() { + mSpinner.setVisibility(View.VISIBLE); + mTest.onSpinner1Setup(mSpinner); + mBar1.setVisibility(View.VISIBLE); mText1.setVisibility(View.VISIBLE); mTest.onBar1Setup(mBar1, mText1); @@ -221,19 +229,21 @@ public class ImageProcessingActivity extends Activity case 27: mTest = new Mandelbrot(); break; + case 28: + mTest = new Blend(); + break; } - mTest.createBaseTest(this, mBitmapIn); + mTest.createBaseTest(this, mBitmapIn, mBitmapIn2); setupBars(); mTest.runTest(); - mTest.updateBitmap(mBitmapOut); - mDisplayView.invalidate(); + updateDisplay(); mBenchmarkResult.setText("Result: not run"); } void setupTests() { - mTestNames = new String[28]; + mTestNames = new String[29]; mTestNames[0] = "Levels Vec3 Relaxed"; mTestNames[1] = "Levels Vec4 Relaxed"; mTestNames[2] = "Levels Vec3 Full"; @@ -262,6 +272,7 @@ public class ImageProcessingActivity extends Activity mTestNames[25] = "Convolve 5x5"; mTestNames[26] = "Intrinsics Convolve 5x5"; mTestNames[27] = "Mandelbrot"; + mTestNames[28] = "Intrinsics Blend"; mTestSpinner.setAdapter(new ArrayAdapter<String>( this, R.layout.spinner_layout, mTestNames)); @@ -284,6 +295,7 @@ public class ImageProcessingActivity extends Activity setContentView(R.layout.main); mBitmapIn = loadBitmap(R.drawable.img1600x1067); + mBitmapIn2 = loadBitmap(R.drawable.img1600x1067b); mBitmapOut = loadBitmap(R.drawable.img1600x1067); mSurfaceView = (SurfaceView) findViewById(R.id.surface); @@ -291,6 +303,8 @@ public class ImageProcessingActivity extends Activity mDisplayView = (ImageView) findViewById(R.id.display); mDisplayView.setImageBitmap(mBitmapOut); + mSpinner = (Spinner) findViewById(R.id.spinner1); + mBar1 = (SeekBar) findViewById(R.id.slider1); mBar2 = (SeekBar) findViewById(R.id.slider2); mBar3 = (SeekBar) findViewById(R.id.slider3); diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java index 6885181535ea..8009daae900d 100644 --- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java +++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java @@ -36,14 +36,18 @@ import android.widget.TextView; import android.view.View; import android.util.Log; import java.lang.Math; +import android.widget.Spinner; public class TestBase { protected final String TAG = "Img"; protected RenderScript mRS; protected Allocation mInPixelsAllocation; + protected Allocation mInPixelsAllocation2; protected Allocation mOutPixelsAllocation; + protected ImageProcessingActivity act; + // Override to use UI elements public void onBar1Changed(int progress) { } @@ -84,11 +88,20 @@ public class TestBase { return false; } - public final void createBaseTest(ImageProcessingActivity act, Bitmap b) { + public boolean onSpinner1Setup(Spinner s) { + s.setVisibility(View.INVISIBLE); + return false; + } + + public final void createBaseTest(ImageProcessingActivity ipact, Bitmap b, Bitmap b2) { + act = ipact; mRS = RenderScript.create(act); mInPixelsAllocation = Allocation.createFromBitmap(mRS, b, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + mInPixelsAllocation2 = Allocation.createFromBitmap(mRS, b2, + Allocation.MipmapControl.MIPMAP_NONE, + Allocation.USAGE_SCRIPT); mOutPixelsAllocation = Allocation.createFromBitmap(mRS, b, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/blend.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/blend.rs new file mode 100644 index 000000000000..87b56f771b22 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/blend.rs @@ -0,0 +1,24 @@ +// Copyright (C) 2011 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. + +#pragma version(1) +#pragma rs java_package_name(com.android.rs.image) + +uchar alpha = 0x0; + +void setImageAlpha(uchar4 *v_out, uint32_t x, uint32_t y) { + v_out->rgba = convert_uchar4((convert_uint4(v_out->rgba) * alpha) >> (uint4)8); + v_out->a = alpha; +} + diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 9c2e1b9127f1..77168f948640 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -1946,7 +1946,7 @@ static status_t writeTextLayoutClasses( const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; fprintf(fp, - "int styleable.%s_%s %d\n", + "int styleable %s_%s %d\n", nclassName.string(), String8(name).string(), (int)pos); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java index 0c85204b581c..0cf0f21ae0a8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java @@ -60,6 +60,11 @@ public class BridgePowerManager implements IPowerManager { } @Override + public void nap(long arg0) throws RemoteException { + // pass for now. + } + + @Override public void preventScreenOn(boolean arg0) throws RemoteException { // pass for now. } |