diff options
58 files changed, 952 insertions, 227 deletions
@@ -69,7 +69,6 @@ LOCAL_SRC_FILES += \ core/java/android/app/IAlarmManager.aidl \ core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ - core/java/android/app/INotificationListener.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/IProcessObserver.aidl \ core/java/android/app/ISearchManager.aidl \ @@ -148,6 +147,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/IUpdateLock.aidl \ core/java/android/os/IUserManager.aidl \ core/java/android/os/IVibratorService.aidl \ + core/java/android/service/notification/INotificationListener.aidl \ core/java/android/service/dreams/IDreamManager.aidl \ core/java/android/service/dreams/IDreamService.aidl \ core/java/android/service/wallpaper/IWallpaperConnection.aidl \ diff --git a/CleanSpec.mk b/CleanSpec.mk index fc63866..4debdc2 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -159,6 +159,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodSession.*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/api/current.txt b/api/current.txt index 653e25a..6456ce9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -22,6 +22,7 @@ package android { field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"; field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD"; + field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE"; @@ -20839,6 +20840,38 @@ package android.service.dreams { } +package android.service.notification { + + public abstract class NotificationListenerService extends android.app.Service { + ctor public NotificationListenerService(); + method public final void clearAllNotifications(); + method public final void clearNotification(java.lang.String, java.lang.String, int); + method public android.os.IBinder onBind(android.content.Intent); + method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification); + method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification); + field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; + } + + public class StatusBarNotification implements android.os.Parcelable { + ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long); + ctor public StatusBarNotification(android.os.Parcel); + method public android.service.notification.StatusBarNotification clone(); + method public int describeContents(); + method public int getUserId(); + method public boolean isClearable(); + method public boolean isOngoing(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public final int id; + field public final android.app.Notification notification; + field public final java.lang.String pkg; + field public final long postTime; + field public final java.lang.String tag; + field public final android.os.UserHandle user; + } + +} + package android.service.textservice { public abstract class SpellCheckerService extends android.app.Service { diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 3d9b2ae..92ec3ad 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -17,12 +17,12 @@ package android.app; -import android.app.INotificationListener; import android.app.ITransientNotification; +import android.service.notification.StatusBarNotification; import android.app.Notification; +import android.content.ComponentName; import android.content.Intent; - -import com.android.internal.statusbar.StatusBarNotification; +import android.service.notification.INotificationListener; /** {@hide} */ interface INotificationManager @@ -41,7 +41,9 @@ interface INotificationManager StatusBarNotification[] getActiveNotifications(String callingPkg); StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count); - void registerListener(in INotificationListener listener, String pkg, int userid); + void registerListener(in INotificationListener listener, in ComponentName component, int userid); void unregisterListener(in INotificationListener listener, int userid); -} + void clearNotificationFromListener(in INotificationListener token, String pkg, String tag, int id); + void clearAllNotificationsFromListener(in INotificationListener token); +}
\ No newline at end of file diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 16b4835..e9e7551 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -152,7 +152,15 @@ public class Binder implements IBinder { * not return until the current process is exiting. */ public static final native void joinThreadPool(); - + + /** + * Returns true if the specified interface is a proxy. + * @hide + */ + public static final boolean isProxy(IInterface iface) { + return iface.asBinder() != iface; + } + /** * Default constructor initializes the object. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 03ee9eb..88ee414 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -655,6 +655,22 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS"; + /** + * Activity Action: Show Notification listener settings. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + * @see android.service.notification.NotificationListenerService + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS + = "android.settings.NOTIFICATION_LISTENER_SETTINGS"; + // End of Intent actions for Settings /** @@ -5398,6 +5414,20 @@ public final class Settings { public static final String CERT_PIN_UPDATE_METADATA_URL = "cert_pin_metadata_url"; /** + * URL for intent firewall updates + * @hide + */ + public static final String INTENT_FIREWALL_UPDATE_CONTENT_URL = + "intent_firewall_content_url"; + + /** + * URL for intent firewall update metadata + * @hide + */ + public static final String INTENT_FIREWALL_UPDATE_METADATA_URL = + "intent_firewall_metadata_url"; + + /** * SELinux enforcement status. If 0, permissive; if 1, enforcing. * @hide */ diff --git a/core/java/android/app/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index f010a2a..425fdc1 100644 --- a/core/java/android/app/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -14,9 +14,9 @@ * limitations under the License. */ -package android.app; +package android.service.notification; -import com.android.internal.statusbar.StatusBarNotification; +import android.service.notification.StatusBarNotification; /** @hide */ oneway interface INotificationListener diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java new file mode 100644 index 0000000..86bab2a --- /dev/null +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2013 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.service.notification; + +import android.annotation.SdkConstant; +import android.app.INotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; + +public abstract class NotificationListenerService extends Service { + // TAG = "NotificationListenerService[MySubclass]" + private final String TAG = NotificationListenerService.class.getSimpleName() + + "[" + getClass().getSimpleName() + "]"; + + private INotificationListenerWrapper mWrapper = null; + + private INotificationManager mNoMan; + + /** + * The {@link Intent} that must be declared as handled by the service. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE + = "android.service.notification.NotificationListenerService"; + + /** + * Implement this method to learn about new notifications as they are posted by apps. + * + * @param sbn A data structure encapsulating the original {@link android.app.Notification} + * object as well as its identifying information (tag and id) and source + * (package name). + */ + public abstract void onNotificationPosted(StatusBarNotification sbn); + + /** + * Implement this method to learn when notifications are removed. + * <P> + * This might occur because the user has dismissed the notification using system UI (or another + * notification listener) or because the app has withdrawn the notification. + * + * @param sbn A data structure encapsulating the original {@link android.app.Notification} + * object as well as its identifying information (tag and id) and source + * (package name). + */ + public abstract void onNotificationRemoved(StatusBarNotification sbn); + + private final INotificationManager getNotificationInterface() { + if (mNoMan == null) { + mNoMan = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + } + return mNoMan; + } + + /** + * Inform the notification manager about dismissal of a single notification. + * <p> + * Use this if your listener has a user interface that allows the user to dismiss individual + * notifications, similar to the behavior of Android's status bar and notification panel. + * It should be called after the user dismisses a single notification using your UI; + * upon being informed, the notification manager will actually remove the notification + * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. + * <P> + * <b>Note:</b> If your listener allows the user to fire a notification's + * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call + * this method at that time <i>if</i> the Notification in question has the + * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set. + * + * @param pkg Package of the notifying app. + * @param tag Tag of the notification as specified by the notifying app in + * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. + * @param id ID of the notification as specified by the notifying app in + * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. + */ + public final void clearNotification(String pkg, String tag, int id) { + try { + getNotificationInterface().clearNotificationFromListener(mWrapper, pkg, tag, id); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + /** + * Inform the notification manager about dismissal of all notifications. + * <p> + * Use this if your listener has a user interface that allows the user to dismiss all + * notifications, similar to the behavior of Android's status bar and notification panel. + * It should be called after the user invokes the "dismiss all" function of your UI; + * upon being informed, the notification manager will actually remove all active notifications + * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks. + * + * {@see #clearNotification(String, String, int)} + */ + public final void clearAllNotifications() { + try { + getNotificationInterface().clearAllNotificationsFromListener(mWrapper); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + @Override + public IBinder onBind(Intent intent) { + if (mWrapper == null) { + mWrapper = new INotificationListenerWrapper(); + } + return mWrapper; + } + + private class INotificationListenerWrapper extends INotificationListener.Stub { + @Override + public void onNotificationPosted(StatusBarNotification sbn) { + NotificationListenerService.this.onNotificationPosted(sbn); + } + @Override + public void onNotificationRemoved(StatusBarNotification sbn) { + NotificationListenerService.this.onNotificationRemoved(sbn); + } + } +} diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.aidl b/core/java/android/service/notification/StatusBarNotification.aidl index bd9e89c..ba81972 100644 --- a/core/java/com/android/internal/statusbar/StatusBarNotification.aidl +++ b/core/java/android/service/notification/StatusBarNotification.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.statusbar; +package android.service.notification; parcelable StatusBarNotification; diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 23e87fc..ef5f8c4 100644 --- a/core/java/com/android/internal/statusbar/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.statusbar; +package android.service.notification; import android.app.Notification; import android.os.Parcel; @@ -23,34 +23,54 @@ import android.os.UserHandle; /** * Class encapsulating a Notification. Sent by the NotificationManagerService to clients including - * the IStatusBar (in System UI). + * the status bar and any {@link android.service.notification.NotificationListenerService}s. */ public class StatusBarNotification implements Parcelable { + /** The package of the app that posted the notification. */ public final String pkg; - public final String basePkg; + /** The id supplied to {@link android.app.NotificationManager#notify}. */ public final int id; + /** The tag supplied to {@link android.app.NotificationManager#notify}, or null if no tag + * was specified. */ public final String tag; + + /** The notifying app's calling uid. @hide */ public final int uid; + /** The notifying app's base package. @hide */ + public final String basePkg; + /** @hide */ public final int initialPid; // TODO: make this field private and move callers to an accessor that // ensures sourceUser is applied. + + /** The {@link android.app.Notification} supplied to + * {@link android.app.NotificationManager#notify}. */ public final Notification notification; - public final int score; + /** The {@link android.os.UserHandle} for whom this notification is intended. */ public final UserHandle user; + /** The time (in {@link System#currentTimeMillis} time) the notification was posted, + * which may be different than {@link android.app.Notification#when}. + */ public final long postTime; - /** This is temporarily needed for the JB MR1 PDK. */ + /** @hide */ + public final int score; + + /** This is temporarily needed for the JB MR1 PDK. + * @hide */ @Deprecated public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score, Notification notification) { this(pkg, id, tag, uid, initialPid, score, notification, UserHandle.OWNER); } + /** @hide */ public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score, Notification notification, UserHandle user) { this(pkg, null, id, tag, uid, initialPid, score, notification, user); } + /** @hide */ public StatusBarNotification(String pkg, String basePkg, int id, String tag, int uid, int initialPid, int score, Notification notification, UserHandle user) { this(pkg, basePkg, id, tag, uid, initialPid, score, notification, user, @@ -147,10 +167,17 @@ public class StatusBarNotification implements Parcelable { this.score, this.notification); } + /** Convenience method to check the notification's flags for + * {@link Notification#FLAG_ONGOING_EVENT}. + */ public boolean isOngoing() { return (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0; } + /** Convenience method to check the notification's flags for + * either {@link Notification#FLAG_ONGOING_EVENT} or + * {@link Notification#FLAG_NO_CLEAR}. + */ public boolean isClearable() { return ((notification.flags & Notification.FLAG_ONGOING_EVENT) == 0) && ((notification.flags & Notification.FLAG_NO_CLEAR) == 0); diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java index a797176..40ee1ad 100644 --- a/core/java/android/view/InputChannel.java +++ b/core/java/android/view/InputChannel.java @@ -54,6 +54,7 @@ public final class InputChannel implements Parcelable { private native void nativeTransferTo(InputChannel other); private native void nativeReadFromParcel(Parcel parcel); private native void nativeWriteToParcel(Parcel parcel); + private native void nativeDup(InputChannel target); private native String nativeGetName(); @@ -64,7 +65,7 @@ public final class InputChannel implements Parcelable { */ public InputChannel() { } - + @Override protected void finalize() throws Throwable { try { @@ -125,6 +126,15 @@ public final class InputChannel implements Parcelable { nativeTransferTo(outParameter); } + /** + * Duplicates the input channel. + */ + public InputChannel dup() { + InputChannel target = new InputChannel(); + nativeDup(target); + return target; + } + @Override public int describeContents() { return Parcelable.CONTENTS_FILE_DESCRIPTOR; diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 18df0b1..00d87bd 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -25,8 +25,13 @@ import dalvik.system.PathClassLoader; /** * Top level factory, used creating all the main WebView implementation classes. + * + * @hide */ -class WebViewFactory { +public final class WebViewFactory { + public static final String WEBVIEW_EXPERIMENTAL_PROPERTY = "persist.sys.webview.exp"; + private static final String DEPRECATED_CHROMIUM_PROPERTY = "webview.use_chromium"; + // Default Provider factory class name. // TODO: When the Chromium powered WebView is ready, it should be the default factory class. private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory"; @@ -43,16 +48,17 @@ class WebViewFactory { private static WebViewFactoryProvider sProviderInstance; private static final Object sProviderLock = new Object(); + public static boolean isExperimentalWebViewAvailable() { + return Build.IS_DEBUGGABLE && (new java.io.File(CHROMIUM_WEBVIEW_JAR).exists()); + } + static WebViewFactoryProvider getProvider() { synchronized (sProviderLock) { // For now the main purpose of this function (and the factory abstraction) is to keep // us honest and minimize usage of WebViewClassic internals when binding the proxy. if (sProviderInstance != null) return sProviderInstance; - // For debug builds, we allow a system property to specify that we should use the - // Chromium powered WebView. This enables us to switch between implementations - // at runtime. For user (release) builds, don't allow this. - if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("webview.use_chromium", false)) { + if (isExperimentalWebViewEnabled()) { StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); try { sProviderInstance = loadChromiumProvider(); @@ -76,6 +82,20 @@ class WebViewFactory { } } + // For debug builds, we allow a system property to specify that we should use the + // experimtanl Chromium powered WebView. This enables us to switch between + // implementations at runtime. For user (release) builds, don't allow this. + private static boolean isExperimentalWebViewEnabled() { + if (!isExperimentalWebViewAvailable()) + return false; + if (SystemProperties.getBoolean(DEPRECATED_CHROMIUM_PROPERTY, false)) { + Log.w(LOGTAG, String.format("The property %s has been deprecated. Please use %s.", + DEPRECATED_CHROMIUM_PROPERTY, WEBVIEW_EXPERIMENTAL_PROPERTY)); + return true; + } + return SystemProperties.getBoolean(WEBVIEW_EXPERIMENTAL_PROPERTY, false); + } + // TODO: This allows us to have the legacy and Chromium WebView coexist for development // and side-by-side testing. After transition, remove this when no longer required. private static WebViewFactoryProvider loadChromiumProvider() { diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 780f5b3..58b15e2 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -17,7 +17,7 @@ package com.android.internal.statusbar; import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.statusbar.StatusBarNotification; +import android.service.notification.StatusBarNotification; /** @hide */ oneway interface IStatusBar diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 04e5bc9..c98ba8d 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -19,7 +19,7 @@ package com.android.internal.statusbar; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; -import com.android.internal.statusbar.StatusBarNotification; +import android.service.notification.StatusBarNotification; /** @hide */ interface IStatusBarService diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java index 9143c61..14afe21 100644 --- a/core/java/com/android/internal/view/InputBindResult.java +++ b/core/java/com/android/internal/view/InputBindResult.java @@ -84,7 +84,7 @@ public final class InputBindResult implements Parcelable { dest.writeStrongInterface(method); if (channel != null) { dest.writeInt(1); - channel.writeToParcel(dest, 0); + channel.writeToParcel(dest, flags); } else { dest.writeInt(0); } diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp index 9c44a59..9fa9fe4 100644 --- a/core/jni/android_view_InputChannel.cpp +++ b/core/jni/android_view_InputChannel.cpp @@ -246,6 +246,15 @@ static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) return name; } +static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) { + NativeInputChannel* nativeInputChannel = + android_view_InputChannel_getNativeInputChannel(env, obj); + if (nativeInputChannel) { + android_view_InputChannel_setNativeInputChannel(env, otherObj, + new NativeInputChannel(nativeInputChannel->getInputChannel()->dup())); + } +} + // ---------------------------------------------------------------------------- static JNINativeMethod gInputChannelMethods[] = { @@ -262,6 +271,8 @@ static JNINativeMethod gInputChannelMethods[] = { (void*)android_view_InputChannel_nativeWriteToParcel }, { "nativeGetName", "()Ljava/lang/String;", (void*)android_view_InputChannel_nativeGetName }, + { "nativeDup", "(Landroid/view/InputChannel;)V", + (void*)android_view_InputChannel_nativeDup }, }; #define FIND_CLASS(var, className) \ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 666d1c6..90e3b8d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2193,6 +2193,14 @@ android:description="@string/permdesc_accessNotifications" android:protectionLevel="signature|system" /> + <!-- Must be required by an {@link + android.service.notification.NotificationListenerService}, + to ensure that only the system can bind to it. --> + <permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" + android:label="@string/permlab_bindNotificationListenerService" + android:description="@string/permdesc_bindNotificationListenerService" + android:protectionLevel="signature" /> + <!-- The system process is explicitly the only one allowed to launch the confirmation UI for full backup/restore --> <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/> @@ -2301,6 +2309,12 @@ </intent-filter> </receiver> + <receiver android:name="com.android.server.updates.IntentFirewallInstallReceiver" > + <intent-filter> + <action android:name="android.intent.action.UPDATE_INTENT_FIREWALL" /> + </intent-filter> + </receiver> + <receiver android:name="com.android.server.updates.SmsShortCodesInstallReceiver" > <intent-filter> <action android:name="android.intent.action.UPDATE_SMS_SHORT_CODES" /> diff --git a/core/res/res/layout/select_dialog.xml b/core/res/res/layout/select_dialog.xml index 80d22f6..eb4d8d9 100644 --- a/core/res/res/layout/select_dialog.xml +++ b/core/res/res/layout/select_dialog.xml @@ -32,4 +32,5 @@ android:cacheColorHint="@null" android:divider="?android:attr/listDividerAlertDialog" android:scrollbars="vertical" - android:overScrollMode="ifContentScrolls" /> + android:overScrollMode="ifContentScrolls" + android:textAlignment="viewStart" /> diff --git a/core/res/res/layout/select_dialog_holo.xml b/core/res/res/layout/select_dialog_holo.xml index 06a5d96..8a92283 100644 --- a/core/res/res/layout/select_dialog_holo.xml +++ b/core/res/res/layout/select_dialog_holo.xml @@ -30,4 +30,5 @@ android:cacheColorHint="@null" android:divider="?android:attr/listDividerAlertDialog" android:scrollbars="vertical" - android:overScrollMode="ifContentScrolls" /> + android:overScrollMode="ifContentScrolls" + android:textAlignment="viewStart" /> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index f72fddb..1e266ed 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toeganklikheid gekanselleer."</string> <string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g> ."</string> <string name="owner_name" msgid="2716755460376028154">"Eienaar"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"Fout"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Hierdie program werk nie met rekeninge vir beperkte gebruikers nie"</string> + <string name="app_not_found" msgid="3429141853498927379">"Geen program gevind om hierdie handeling te hanteer nie"</string> </resources> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 73c323c..54dfd50 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"ተደራሽነት ተሰርዟል።"</string> <string name="user_switched" msgid="3768006783166984410">"የአሁኑ ተጠቃሚ <xliff:g id="NAME">%1$s</xliff:g>።"</string> <string name="owner_name" msgid="2716755460376028154">"ባለቤት"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"ስህተት"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"ይህ መተግበሪያ የተገደቡ ተጠቃሚዎች መለያዎችን አይደግፍም"</string> + <string name="app_not_found" msgid="3429141853498927379">"ይህን እርምጃ የሚያከናውን ምንም መተግበሪያ አልተገኘም"</string> </resources> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 942968b..0c6cd78 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"تم إلغاء تسهيل الدخول."</string> <string name="user_switched" msgid="3768006783166984410">"المستخدم الحالي <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"المالك"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"خطأ"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"لا يوفر هذا التطبيق حسابات للمستخدمين المقيّدين"</string> + <string name="app_not_found" msgid="3429141853498927379">"لم يتم العثور على تطبيق يمكنه التعامل مع هذا الإجراء."</string> </resources> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index bb4eb72..d2676c9 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Tilgængelighed er annulleret."</string> <string name="user_switched" msgid="3768006783166984410">"Nuværende bruger <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Ejer"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"Fejl"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Denne applikation understøtter ikke konti for brugere med begrænsede rettigheder"</string> + <string name="app_not_found" msgid="3429141853498927379">"Der blev ikke fundet nogen applikation, der kan håndtere denne handling"</string> </resources> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 61d768d..f3f0afc 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Se canceló la accesibilidad."</string> <string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="owner_name" msgid="2716755460376028154">"Propietario"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"Error"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Esta aplicación no admite cuentas para usuarios restringidos."</string> + <string name="app_not_found" msgid="3429141853498927379">"No se encontró una aplicación para manejar esta acción."</string> </resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index bdeed44..d3e63af 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accesibilidad cancelada"</string> <string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="owner_name" msgid="2716755460376028154">"Propietario"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"Error"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Esta aplicación no admite cuentas de usuarios limitados."</string> + <string name="app_not_found" msgid="3429141853498927379">"No se ha encontrado ninguna aplicación que pueda realizar esta acción."</string> </resources> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 9c7a28f..bb00bfc 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -1477,6 +1477,6 @@ <string name="user_switched" msgid="3768006783166984410">"Praegune kasutaja <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Omanik"</string> <string name="error_message_title" msgid="4510373083082500195">"Viga"</string> - <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Rakendus ei toeta piiratud arvu kasutajate kontot"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Rakendus ei toeta piiratud õigustega kasutajate kontosid"</string> <string name="app_not_found" msgid="3429141853498927379">"Selle toimingu käsitlemiseks ei leitud ühtegi rakendust"</string> </resources> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 4a09361..322cbee 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"قابلیت دسترسی لغو شد."</string> <string name="user_switched" msgid="3768006783166984410">"کاربر کنونی <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"دارنده"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"خطا"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"این برنامه حسابهای تعداد محدودی از کاربران را پشتیبانی نمیکند"</string> + <string name="app_not_found" msgid="3429141853498927379">"برنامهای برای انجام این عملکرد موجود نیست"</string> </resources> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index b70bbc0..1b3e6d7 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"पहुंच-योग्यता रद्द की गई."</string> <string name="user_switched" msgid="3768006783166984410">"वर्तमान उपयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"स्वामी"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"त्रुटि"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"यह एप्लिकेशन सीमित उपयोगकर्ताओं के खातों का समर्थन नहीं करता है"</string> + <string name="app_not_found" msgid="3429141853498927379">"इस कार्यवाही को प्रबंधित करने के लिए कोई एप्लिकेशन नहीं मिला"</string> </resources> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 8d3122f..0414be8 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -744,7 +744,7 @@ <string name="relationTypeDomesticPartner" msgid="6904807112121122133">"Partner życiowy"</string> <string name="relationTypeFather" msgid="5228034687082050725">"Ojciec"</string> <string name="relationTypeFriend" msgid="7313106762483391262">"Znajomy"</string> - <string name="relationTypeManager" msgid="6365677861610137895">"Kierownik"</string> + <string name="relationTypeManager" msgid="6365677861610137895">"Menedżer"</string> <string name="relationTypeMother" msgid="4578571352962758304">"Matka"</string> <string name="relationTypeParent" msgid="4755635567562925226">"Rodzic"</string> <string name="relationTypePartner" msgid="7266490285120262781">"Partner"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 96b013f..67b4608 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -1350,7 +1350,7 @@ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Imefanyika"</string> <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Modi ya mabadiliko"</string> <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Songa"</string> - <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Ingiza"</string> + <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string> <string name="activitychooserview_choose_application" msgid="2125168057199941199">"Chagua programu"</string> <string name="shareactionprovider_share_with" msgid="806688056141131819">"Shiriki na"</string> <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Shiriki na <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ufikivu umeghairiwa."</string> <string name="user_switched" msgid="3768006783166984410">"Mtumiaji wa sasa <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Mmiliki"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"Hitilafu"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Programu hii haiwezi kutumiwa na akaunti za watumiaji waliowekewa vizuizi"</string> + <string name="app_not_found" msgid="3429141853498927379">"Hakuna programu iliyopatikana ili kushughulikia kitendo hiki"</string> </resources> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 43fdeac..2d56e38 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -154,9 +154,9 @@ <string name="global_actions" product="default" msgid="2406416831541615258">"ตัวเลือกโทรศัพท์"</string> <string name="global_action_lock" msgid="2844945191792119712">"ล็อกหน้าจอ"</string> <string name="global_action_power_off" msgid="4471879440839879722">"ปิดเครื่อง"</string> - <string name="global_action_bug_report" msgid="7934010578922304799">"รายงานข้อบกพร่อง"</string> - <string name="bugreport_title" msgid="2667494803742548533">"ใช้รายงานข้อบกพร่อง"</string> - <string name="bugreport_message" msgid="398447048750350456">"การดำเนินการนี้จะรวบรวมข้อมูลเกี่ยวกับสถานะปัจจุบันของอุปกรณ์ของคุณ โดยจะส่งไปในรูปแบบข้อความอีเมล อาจใช้เวลาสักครู่ตั้งแต่เริ่มการสร้างรายงานข้อบกพร่องจนกระทั่งเสร็จสมบูรณ์ โปรดอดทนรอ"</string> + <string name="global_action_bug_report" msgid="7934010578922304799">"รายงานบั๊ก"</string> + <string name="bugreport_title" msgid="2667494803742548533">"ใช้รายงานบั๊ก"</string> + <string name="bugreport_message" msgid="398447048750350456">"การดำเนินการนี้จะรวบรวมข้อมูลเกี่ยวกับสถานะปัจจุบันของอุปกรณ์ของคุณ โดยจะส่งไปในรูปแบบข้อความอีเมล อาจใช้เวลาสักครู่ตั้งแต่เริ่มการสร้างรายงานบั๊กจนกระทั่งเสร็จสมบูรณ์ โปรดอดทนรอ"</string> <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"โหมดปิดเสียง"</string> <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"ปิดเสียงไว้"</string> <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"เปิดเสียงแล้ว"</string> @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"ยกเลิกการเข้าถึงแล้ว"</string> <string name="user_switched" msgid="3768006783166984410">"ผู้ใช้ปัจจุบัน <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="owner_name" msgid="2716755460376028154">"เจ้าของ"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"ข้อผิดพลาด"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"แอปพลิเคชันนี้ไม่สนับสนุนบัญชีของผู้ใช้บางรายที่ถูกจำกัด"</string> + <string name="app_not_found" msgid="3429141853498927379">"ไม่พบแอปพลิเคชันสำหรับการทำงานนี้"</string> </resources> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 2fddf7d..c8d3ae4 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Erişilebilirlik iptal edildi."</string> <string name="user_switched" msgid="3768006783166984410">"Geçerli kullanıcı: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Sahibi"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"Hata"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Bu uygulama, kısıtlı kullanıcı hesaplarını desteklemiyor"</string> + <string name="app_not_found" msgid="3429141853498927379">"Bu eylemi gerçekleştirecek bir uygulama bulunamadı"</string> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index ed2551d..cea9369 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1478,5 +1478,5 @@ <string name="owner_name" msgid="2716755460376028154">"擁有者"</string> <string name="error_message_title" msgid="4510373083082500195">"錯誤"</string> <string name="app_no_restricted_accounts" msgid="5322164210667258876">"這個應用程式不支援受限的使用者帳戶。"</string> - <string name="app_not_found" msgid="3429141853498927379">"找不到可以處理這個動作的應用程式"</string> + <string name="app_not_found" msgid="3429141853498927379">"找不到支援此操作的應用程式"</string> </resources> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index a104729..8bf3aae 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1476,10 +1476,7 @@ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ukufinyelela kukhanseliwe."</string> <string name="user_switched" msgid="3768006783166984410">"Umsebenzisi wamanje <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Umnikazi"</string> - <!-- no translation found for error_message_title (4510373083082500195) --> - <skip /> - <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> - <skip /> - <!-- no translation found for app_not_found (3429141853498927379) --> - <skip /> + <string name="error_message_title" msgid="4510373083082500195">"Iphutha"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Lolu hlelo lokusebenza alusekeli ama-akhawunti wabasebenzisi abakhawulelwe"</string> + <string name="app_not_found" msgid="3429141853498927379">"Alukho uhlelo lokusebenza olutholakele lokuphatha lesi senzo"</string> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4a15967..6bf6403 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1816,6 +1816,11 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_accessNotifications">Allows the app to retrieve, examine, and clear notifications, including those posted by other apps.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_bindNotificationListenerService">bind to a notification listener service</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_bindNotificationListenerService">Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps.</string> + <!-- Policy administration --> <!-- Title of policy access to limiting the user's password choices --> @@ -3508,6 +3513,9 @@ <string name="wallpaper_binding_label">Wallpaper</string> <!-- Dialog title for user to select a different wallpaper from service list --> <string name="chooser_wallpaper">Change wallpaper</string> + <!-- Label to show for a service that is running because it is observing + the user's notifications. --> + <string name="notification_listener_binding_label">Notification listener</string> <!-- Do Not Translate: Alternate eri.xml --> <string name="alternate_eri_file">/data/eri.xml</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ced0851..bb35bab 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1638,6 +1638,7 @@ <java-symbol type="string" name="launch_warning_title" /> <java-symbol type="string" name="low_internal_storage_view_text" /> <java-symbol type="string" name="low_internal_storage_view_title" /> + <java-symbol type="string" name="notification_listener_binding_label" /> <java-symbol type="string" name="report" /> <java-symbol type="string" name="select_input_method" /> <java-symbol type="string" name="select_keyboard_layout_notification_title" /> diff --git a/include/androidfw/InputTransport.h b/include/androidfw/InputTransport.h index 5706bce..8712995 100644 --- a/include/androidfw/InputTransport.h +++ b/include/androidfw/InputTransport.h @@ -168,6 +168,9 @@ public: */ status_t receiveMessage(InputMessage* msg); + /* Returns a new object that has a duplicate of this channel's fd. */ + sp<InputChannel> dup() const; + private: String8 mName; int mFd; diff --git a/libs/androidfw/InputTransport.cpp b/libs/androidfw/InputTransport.cpp index 351c666..498389e 100644 --- a/libs/androidfw/InputTransport.cpp +++ b/libs/androidfw/InputTransport.cpp @@ -219,6 +219,11 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { return OK; } +sp<InputChannel> InputChannel::dup() const { + int fd = ::dup(getFd()); + return fd >= 0 ? new InputChannel(getName(), fd) : NULL; +} + // --- InputPublisher --- diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index cd50de4..399eb7b 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -791,7 +791,7 @@ public class AudioTrack * {@link #ERROR_INVALID_OPERATION} */ public int setPlaybackRate(int sampleRateInHz) { - if (mState == STATE_UNINITIALIZED) { + if (mState != STATE_INITIALIZED) { return ERROR_INVALID_OPERATION; } if (sampleRateInHz <= 0) { diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 3cdf261..4eb0c56 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Bundle; +import android.os.Parcel; import android.util.Log; /** @@ -136,10 +137,8 @@ public final class MediaDrm { public static final int MEDIA_DRM_EVENT_KEY_EXPIRED = 3; public static final int MEDIA_DRM_EVENT_VENDOR_DEFINED = 4; - /* Do not change these values without updating their counterparts - * in include/media/mediadrm.h! - */ private static final int DRM_EVENT = 200; + private class EventHandler extends Handler { private MediaDrm mMediaDrm; @@ -161,10 +160,18 @@ public final class MediaDrm { Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")"); if (mOnEventListener != null) { - Bundle bundle = msg.getData(); - byte[] sessionId = bundle.getByteArray("sessionId"); - byte[] data = bundle.getByteArray("data"); - mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data); + if (msg.obj != null && msg.obj instanceof Parcel) { + Parcel parcel = (Parcel)msg.obj; + byte[] sessionId = parcel.createByteArray(); + if (sessionId.length == 0) { + sessionId = null; + } + byte[] data = parcel.createByteArray(); + if (data.length == 0) { + data = null; + } + mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data); + } } return; @@ -183,14 +190,14 @@ public final class MediaDrm { * the cookie passed to native_setup().) */ private static void postEventFromNative(Object mediadrm_ref, - int what, int arg1, int arg2, Object obj) + int eventType, int extra, Object obj) { MediaDrm md = (MediaDrm)((WeakReference)mediadrm_ref).get(); if (md == null) { return; } if (md.mEventHandler != null) { - Message m = md.mEventHandler.obtainMessage(what, arg1, arg2, obj); + Message m = md.mEventHandler.obtainMessage(DRM_EVENT, eventType, extra, obj); md.mEventHandler.sendMessage(m); } } diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index 1618edf..c32ba9d 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -21,10 +21,12 @@ #include "android_media_MediaDrm.h" #include "android_runtime/AndroidRuntime.h" +#include "android_os_Parcel.h" #include "jni.h" #include "JNIHelp.h" #include <binder/IServiceManager.h> +#include <binder/Parcel.h> #include <media/IDrm.h> #include <media/IMediaPlayerService.h> #include <media/stagefright/foundation/ADebug.h> @@ -43,6 +45,15 @@ namespace android { var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \ LOG_FATAL_IF(! var, "Unable to find method " fieldName); +#define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +#define GET_STATIC_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetStaticMethodID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find static method " fieldName); + + struct RequestFields { jfieldID data; jfieldID defaultUrl; @@ -74,8 +85,16 @@ struct EntryFields { jmethodID getValue; }; +struct EventTypes { + int kEventProvisionRequired; + int kEventKeyRequired; + int kEventKeyExpired; + int kEventVendorDefined; +} gEventTypes; + struct fields_t { jfieldID context; + jmethodID post_event; RequestFields keyRequest; RequestFields provisionRequest; ArrayListFields arraylist; @@ -87,6 +106,88 @@ struct fields_t { static fields_t gFields; +// ---------------------------------------------------------------------------- +// ref-counted object for callbacks +class JNIDrmListener: public DrmListener +{ +public: + JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz); + ~JNIDrmListener(); + virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj = NULL); +private: + JNIDrmListener(); + jclass mClass; // Reference to MediaDrm class + jobject mObject; // Weak ref to MediaDrm Java object to call on +}; + +JNIDrmListener::JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz) +{ + // Hold onto the MediaDrm class for use in calling the static method + // that posts events to the application thread. + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + ALOGE("Can't find android/media/MediaDrm"); + jniThrowException(env, "java/lang/Exception", NULL); + return; + } + mClass = (jclass)env->NewGlobalRef(clazz); + + // We use a weak reference so the MediaDrm object can be garbage collected. + // The reference is only used as a proxy for callbacks. + mObject = env->NewGlobalRef(weak_thiz); +} + +JNIDrmListener::~JNIDrmListener() +{ + // remove global references + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mObject); + env->DeleteGlobalRef(mClass); +} + +void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra, + const Parcel *obj) +{ + jint jeventType; + + // translate DrmPlugin event types into their java equivalents + switch(eventType) { + case DrmPlugin::kDrmPluginEventProvisionRequired: + jeventType = gEventTypes.kEventProvisionRequired; + break; + case DrmPlugin::kDrmPluginEventKeyNeeded: + jeventType = gEventTypes.kEventKeyRequired; + break; + case DrmPlugin::kDrmPluginEventKeyExpired: + jeventType = gEventTypes.kEventKeyExpired; + break; + case DrmPlugin::kDrmPluginEventVendorDefined: + jeventType = gEventTypes.kEventVendorDefined; + break; + default: + ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType); + return; + } + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (obj && obj->dataSize() > 0) { + jobject jParcel = createJavaParcelObject(env); + if (jParcel != NULL) { + Parcel* nativeParcel = parcelForJavaObject(env, jParcel); + nativeParcel->setData(obj->data(), obj->dataSize()); + env->CallStaticVoidMethod(mClass, gFields.post_event, mObject, + jeventType, extra, jParcel); + } + } + + if (env->ExceptionCheck()) { + ALOGW("An exception occurred while notifying an event."); + LOGW_EX(env); + env->ExceptionClear(); + } +} + + static bool throwExceptionAsNecessary( JNIEnv *env, status_t err, const char *msg = NULL) { @@ -109,6 +210,9 @@ JDrm::JDrm( JNIEnv *env, jobject thiz, const uint8_t uuid[16]) { mObject = env->NewWeakGlobalRef(thiz); mDrm = MakeDrm(uuid); + if (mDrm != NULL) { + mDrm->setListener(this); + } } JDrm::~JDrm() { @@ -160,6 +264,25 @@ sp<IDrm> JDrm::MakeDrm(const uint8_t uuid[16]) { return drm; } +status_t JDrm::setListener(const sp<DrmListener>& listener) { + Mutex::Autolock lock(mLock); + mListener = listener; + return OK; +} + +void JDrm::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) { + sp<DrmListener> listener; + mLock.lock(); + listener = mListener; + mLock.unlock(); + + if (listener != NULL) { + Mutex::Autolock lock(mNotifyLock); + listener->notify(eventType, extra, obj); + } +} + + // static bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16]) { sp<IDrm> drm = MakeDrm(); @@ -194,10 +317,9 @@ static jbyteArray VectorToJByteArray(JNIEnv *env, Vector<uint8_t> const &vector) } static String8 JStringToString8(JNIEnv *env, jstring const &jstr) { - jboolean isCopy; String8 result; - const char *s = env->GetStringUTFChars(jstr, &isCopy); + const char *s = env->GetStringUTFChars(jstr, NULL); if (s) { result = s; env->ReleaseStringUTFChars(jstr, s); @@ -322,13 +444,28 @@ static bool CheckSession(JNIEnv *env, const sp<IDrm> &drm, jbyteArray const &jse } static void android_media_MediaDrm_release(JNIEnv *env, jobject thiz) { - setDrm(env, thiz, NULL); + sp<JDrm> drm = setDrm(env, thiz, NULL); + if (drm != NULL) { + drm->setListener(NULL); + } } static void android_media_MediaDrm_native_init(JNIEnv *env) { jclass clazz; FIND_CLASS(clazz, "android/media/MediaDrm"); GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "I"); + GET_STATIC_METHOD_ID(gFields.post_event, clazz, "postEventFromNative", + "(Ljava/lang/Object;IILjava/lang/Object;)V"); + + jfieldID field; + GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_PROVISION_REQUIRED", "I"); + gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field); + GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_KEY_REQUIRED", "I"); + gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field); + GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_KEY_EXPIRED", "I"); + gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field); + GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_VENDOR_DEFINED", "I"); + gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field); FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); GET_FIELD_ID(gFields.keyRequest.data, clazz, "data", "[B"); @@ -389,6 +526,8 @@ static void android_media_MediaDrm_native_setup( return; } + sp<JNIDrmListener> listener = new JNIDrmListener(env, thiz, weak_this); + drm->setListener(listener); setDrm(env, thiz, drm); } diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h index 01067c4..9b3917f 100644 --- a/media/jni/android_media_MediaDrm.h +++ b/media/jni/android_media_MediaDrm.h @@ -20,6 +20,8 @@ #include "jni.h" #include <media/stagefright/foundation/ABase.h> +#include <media/IDrm.h> +#include <media/IDrmClient.h> #include <utils/Errors.h> #include <utils/RefBase.h> @@ -27,15 +29,24 @@ namespace android { struct IDrm; -struct JDrm : public RefBase { +class DrmListener: virtual public RefBase +{ +public: + virtual void notify(DrmPlugin::EventType eventType, int extra, + const Parcel *obj) = 0; +}; + +struct JDrm : public BnDrmClient { static bool IsCryptoSchemeSupported(const uint8_t uuid[16]); JDrm(JNIEnv *env, jobject thiz, const uint8_t uuid[16]); status_t initCheck() const; - sp<IDrm> getDrm() { return mDrm; } + void notify(DrmPlugin::EventType, int extra, const Parcel *obj); + status_t setListener(const sp<DrmListener>& listener); + protected: virtual ~JDrm(); @@ -43,6 +54,10 @@ private: jweak mObject; sp<IDrm> mDrm; + sp<DrmListener> mListener; + Mutex mNotifyLock; + Mutex mLock; + static sp<IDrm> MakeDrm(); static sp<IDrm> MakeDrm(const uint8_t uuid[16]); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 7bdcf6e..5b911c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -17,11 +17,11 @@ package com.android.systemui.statusbar; +import android.service.notification.StatusBarNotification; import android.content.res.Configuration; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; -import com.android.internal.statusbar.StatusBarNotification; import com.android.internal.widget.SizeAdaptiveLayout; import com.android.systemui.R; import com.android.systemui.SearchPanelView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 752bb0c..cbbaab3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -20,10 +20,10 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.service.notification.StatusBarNotification; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; -import com.android.internal.statusbar.StatusBarNotification; /** * This class takes the functions from IStatusBar that come in on diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index c82f250..886ed77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -16,12 +16,11 @@ package com.android.systemui.statusbar; -import android.app.Notification; +import android.service.notification.StatusBarNotification; import android.os.IBinder; import android.view.View; import android.widget.ImageView; -import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.R; import java.util.Comparator; 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 9f54573..52f552b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -26,6 +26,7 @@ import android.app.ActivityManagerNative; import android.app.Notification; import android.app.PendingIntent; import android.app.StatusBarManager; +import android.service.notification.StatusBarNotification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -76,7 +77,6 @@ import android.widget.ScrollView; import android.widget.TextView; import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.EventLogTags; import com.android.systemui.R; import com.android.systemui.statusbar.BaseStatusBar; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java index ecc70d6..976dd01 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import android.service.notification.StatusBarNotification; import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; @@ -23,10 +24,7 @@ import android.os.Handler; import android.text.StaticLayout; import android.text.Layout.Alignment; import android.text.TextPaint; -import android.text.TextUtils; -import android.util.Slog; import android.view.View; -import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.TextSwitcher; import android.widget.TextView; @@ -35,7 +33,6 @@ import android.widget.ImageSwitcher; import java.util.ArrayList; import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.statusbar.StatusBarNotification; import com.android.internal.util.CharSequences; import com.android.systemui.R; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java index 0944b40..68d048d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java @@ -28,16 +28,11 @@ import android.content.IntentFilter; import android.location.LocationManager; import android.os.UserHandle; import android.provider.Settings; -import android.util.Slog; -import android.view.View; -import android.widget.ImageView; // private NM API import android.app.INotificationManager; -import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.R; -import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; public class LocationController extends BroadcastReceiver { private static final String TAG = "StatusBar.LocationController"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index 3d6bfe7..05bba89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -23,6 +23,7 @@ import android.app.ActivityManagerNative; import android.app.Notification; import android.app.PendingIntent; import android.app.StatusBarManager; +import android.service.notification.StatusBarNotification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -58,7 +59,6 @@ import android.widget.ScrollView; import android.widget.TextView; import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.R; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java index 0859874..725d9e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java @@ -21,9 +21,9 @@ import java.util.Arrays; import android.animation.LayoutTransition; import android.app.Notification; import android.app.PendingIntent; +import android.service.notification.StatusBarNotification; import android.content.Context; import android.content.res.Resources; -import android.graphics.Bitmap; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -37,11 +37,9 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.FrameLayout; import android.widget.TextView; import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.R; import com.android.systemui.statusbar.StatusBarIconView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 413cc78..dc5de02 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.tv; +import android.service.notification.StatusBarNotification; import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.statusbar.BaseStatusBar; import android.os.IBinder; diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 3b541ec..a28c387 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1070,7 +1070,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); showCurrentInputLocked(getAppShowFlags(), null); } - return new InputBindResult(session.session, session.channel, mCurId, mCurSeq); + return new InputBindResult(session.session, + session.channel != null ? session.channel.dup() : null, mCurId, mCurSeq); } InputBindResult startInputLocked(IInputMethodClient client, @@ -2357,13 +2358,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return true; case MSG_CREATE_SESSION: { args = (SomeArgs)msg.obj; + IInputMethod method = (IInputMethod)args.arg1; InputChannel channel = (InputChannel)args.arg2; try { - ((IInputMethod)args.arg1).createSession(channel, - (IInputSessionCallback)args.arg3); + method.createSession(channel, (IInputSessionCallback)args.arg3); } catch (RemoteException e) { } finally { - if (channel != null) { + // Dispose the channel if the input method is not local to this process + // because the remote proxy will get its own copy when unparceled. + if (channel != null && Binder.isProxy(method)) { channel.dispose(); } } @@ -2404,16 +2407,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // There is nothing interesting about the last client dying. } return true; - case MSG_BIND_METHOD: + case MSG_BIND_METHOD: { args = (SomeArgs)msg.obj; + IInputMethodClient client = (IInputMethodClient)args.arg1; + InputBindResult res = (InputBindResult)args.arg2; try { - ((IInputMethodClient)args.arg1).onBindMethod( - (InputBindResult)args.arg2); + client.onBindMethod(res); } catch (RemoteException e) { Slog.w(TAG, "Client died receiving input method " + args.arg2); + } finally { + // Dispose the channel if the input method is not local to this process + // because the remote proxy will get its own copy when unparceled. + if (res.channel != null && Binder.isProxy(client)) { + res.channel.dispose(); + } } args.recycle(); return true; + } case MSG_SET_ACTIVE: try { ((ClientState)msg.obj).client.setActive(msg.arg1 != 0); diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 44d730c..cfb892f 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -26,16 +26,17 @@ import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.INotificationManager; -import android.app.INotificationListener; import android.app.ITransientNotification; import android.app.Notification; import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -57,6 +58,9 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; +import android.service.notification.INotificationListener; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.AtomicFile; @@ -68,8 +72,6 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; -import com.android.internal.statusbar.StatusBarNotification; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -121,6 +123,8 @@ public class NotificationManagerService extends INotificationManager.Stub private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; private static final boolean ENABLE_BLOCKED_TOASTS = true; + private static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":"; + final Context mContext; final IActivityManager mAm; final UserManager mUserManager; @@ -163,8 +167,18 @@ public class NotificationManagerService extends INotificationManager.Stub private final AppOpsManager mAppOps; - private ArrayList<NotificationListenerInfo> mListeners = new ArrayList<NotificationListenerInfo>(); - private ArrayList<String> mEnabledListenersForCurrentUser = new ArrayList<String>(); + // contains connections to all connected listeners, including app services + // and system listeners + private ArrayList<NotificationListenerInfo> mListeners + = new ArrayList<NotificationListenerInfo>(); + // things that will be put into mListeners as soon as they're ready + private ArrayList<String> mServicesBinding = new ArrayList<String>(); + // lists the component names of all enabled (and therefore connected) listener + // app services for the current user only + private HashSet<ComponentName> mEnabledListenersForCurrentUser + = new HashSet<ComponentName>(); + // Just the packages from mEnabledListenersForCurrentUser + private HashSet<String> mEnabledListenerPackageNames = new HashSet<String>(); // Notification control database. For now just contains disabled packages. private AtomicFile mPolicyFile; @@ -181,27 +195,42 @@ public class NotificationManagerService extends INotificationManager.Stub private class NotificationListenerInfo implements DeathRecipient { INotificationListener listener; - String pkg; + ComponentName component; int userid; boolean isSystem; + ServiceConnection connection; - public NotificationListenerInfo(INotificationListener listener, String pkg, int userid, - boolean isSystem) { + public NotificationListenerInfo(INotificationListener listener, ComponentName component, + int userid, boolean isSystem) { this.listener = listener; - this.pkg = pkg; + this.component = component; this.userid = userid; this.isSystem = isSystem; + this.connection = null; + } + + public NotificationListenerInfo(INotificationListener listener, ComponentName component, + int userid, ServiceConnection connection) { + this.listener = listener; + this.component = component; + this.userid = userid; + this.isSystem = false; + this.connection = connection; } boolean enabledAndUserMatches(StatusBarNotification sbn) { final int nid = sbn.getUserId(); - if (!(isSystem || isEnabledForUser(nid))) return false; + if (!isEnabledForCurrentUser()) { + return false; + } if (this.userid == UserHandle.USER_ALL) return true; return (nid == UserHandle.USER_ALL || nid == this.userid); } public void notifyPostedIfUserMatch(StatusBarNotification sbn) { - if (!enabledAndUserMatches(sbn)) return; + if (!enabledAndUserMatches(sbn)) { + return; + } try { listener.onNotificationPosted(sbn); } catch (RemoteException ex) { @@ -220,15 +249,17 @@ public class NotificationManagerService extends INotificationManager.Stub @Override public void binderDied() { - unregisterListener(this.listener, this.userid); + if (connection == null) { + // This is not a service; it won't be recreated. We can give up this connection. + unregisterListener(this.listener, this.userid); + } } /** convenience method for looking in mEnabledListenersForCurrentUser */ - public boolean isEnabledForUser(int userid) { - for (int i=0; i<mEnabledListenersForCurrentUser.size(); i++) { - if (this.pkg.equals(mEnabledListenersForCurrentUser.get(i))) return true; - } - return false; + public boolean isEnabledForCurrentUser() { + if (this.isSystem) return true; + if (this.connection == null) return false; + return mEnabledListenersForCurrentUser.contains(this.component); } } @@ -434,6 +465,12 @@ public class NotificationManagerService extends INotificationManager.Stub } } + /** + * System-only API for getting a list of current (i.e. not cleared) notifications. + * + * Requires ACCESS_NOTIFICATIONS which is signature|system. + */ + @Override public StatusBarNotification[] getActiveNotifications(String callingPkg) { // enforce() will ensure the calling uid has the correct permission mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS, @@ -456,6 +493,12 @@ public class NotificationManagerService extends INotificationManager.Stub return tmp; } + /** + * System-only API for getting a list of recent (cleared, no longer shown) notifications. + * + * Requires ACCESS_NOTIFICATIONS which is signature|system. + */ + @Override public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { // enforce() will ensure the calling uid has the correct permission mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS, @@ -474,27 +517,76 @@ public class NotificationManagerService extends INotificationManager.Stub return tmp; } - boolean packageCanTapNotificationsForUser(final int uid, final String pkg) { - // Make sure the package and uid match, and that the package is allowed access - return (AppOpsManager.MODE_ALLOWED - == mAppOps.checkOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, pkg)); + /** + * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS + * is altered. (For example in response to USER_SWITCHED in our broadcast receiver) + */ + void rebindListenerServices() { + String flat = Settings.Secure.getString( + mContext.getContentResolver(), + Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + + NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()]; + final ArrayList<ComponentName> toAdd; + final int currentUser = ActivityManager.getCurrentUser(); + + synchronized (mNotificationList) { + // unbind and remove all existing listeners + toRemove = mListeners.toArray(toRemove); + + toAdd = new ArrayList<ComponentName>(); + final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>(); + final HashSet<String> newPackages = new HashSet<String>(); + + // decode the list of components + if (flat != null) { + String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR); + for (int i=0; i<components.length; i++) { + final ComponentName component + = ComponentName.unflattenFromString(components[i]); + if (component != null) { + newEnabled.add(component); + toAdd.add(component); + newPackages.add(component.getPackageName()); + } + } + + mEnabledListenersForCurrentUser = newEnabled; + mEnabledListenerPackageNames = newPackages; + } + } + + for (NotificationListenerInfo info : toRemove) { + final ComponentName component = info.component; + final int oldUser = info.userid; + Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component); + unregisterListenerService(component, info.userid); + } + + final int N = toAdd.size(); + for (int i=0; i<N; i++) { + final ComponentName component = toAdd.get(i); + Slog.v(TAG, "enabling notification listener for user " + currentUser + ": " + + component); + registerListenerService(component, currentUser); + } } + /** + * Register a listener binder directly with the notification manager. + * + * Only works with system callers. Apps should extend + * {@link android.service.notification.NotificationListenerService}. + */ @Override public void registerListener(final INotificationListener listener, - final String pkg, final int userid) { - // ensure system or allowed pkg - int uid = Binder.getCallingUid(); - boolean isSystem = (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0); - if (!(isSystem || packageCanTapNotificationsForUser(uid, pkg))) { - throw new SecurityException("Package " + pkg - + " may not listen for notifications"); - } + final ComponentName component, final int userid) { + checkCallerIsSystem(); synchronized (mNotificationList) { try { NotificationListenerInfo info - = new NotificationListenerInfo(listener, pkg, userid, isSystem); + = new NotificationListenerInfo(listener, component, userid, true); listener.asBinder().linkToDeath(info, 0); mListeners.add(info); } catch (RemoteException e) { @@ -503,6 +595,90 @@ public class NotificationManagerService extends INotificationManager.Stub } } + /** + * Version of registerListener that takes the name of a + * {@link android.service.notification.NotificationListenerService} to bind to. + * + * This is the mechanism by which third parties may subscribe to notifications. + */ + private void registerListenerService(final ComponentName name, final int userid) { + checkCallerIsSystem(); + + if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid); + + synchronized (mNotificationList) { + final String servicesBindingTag = name.toString() + "/" + userid; + if (mServicesBinding.contains(servicesBindingTag)) { + // stop registering this thing already! we're working on it + return; + } + mServicesBinding.add(servicesBindingTag); + + final int N = mListeners.size(); + for (int i=N-1; i>=0; i--) { + final NotificationListenerInfo info = mListeners.get(i); + if (name.equals(info.component) + && info.userid == userid) { + // cut old connections + if (DBG) Slog.v(TAG, " disconnecting old listener: " + info.listener); + mListeners.remove(i); + if (info.connection != null) { + mContext.unbindService(info.connection); + } + } + } + + Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE); + intent.setComponent(name); + + intent.putExtra(Intent.EXTRA_CLIENT_LABEL, + com.android.internal.R.string.notification_listener_binding_label); + intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( + mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0)); + + try { + if (DBG) Slog.v(TAG, "binding: " + intent); + if (!mContext.bindServiceAsUser(intent, + new ServiceConnection() { + INotificationListener mListener; + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mNotificationList) { + mServicesBinding.remove(servicesBindingTag); + try { + mListener = INotificationListener.Stub.asInterface(service); + NotificationListenerInfo info = new NotificationListenerInfo( + mListener, name, userid, this); + service.linkToDeath(info, 0); + mListeners.add(info); + } catch (RemoteException e) { + // already dead + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + Slog.v(TAG, "notification listener connection lost: " + name); + } + }, + Context.BIND_AUTO_CREATE, + new UserHandle(userid))) + { + mServicesBinding.remove(servicesBindingTag); + Slog.w(TAG, "Unable to bind listener service: " + intent); + return; + } + } catch (SecurityException ex) { + Slog.e(TAG, "Unable to bind listener service: " + intent, ex); + return; + } + } + } + + /** + * Remove a listener binder directly + */ @Override public void unregisterListener(INotificationListener listener, int userid) { // no need to check permissions; if your listener binder is in the list, @@ -513,12 +689,39 @@ public class NotificationManagerService extends INotificationManager.Stub for (int i=N-1; i>=0; i--) { final NotificationListenerInfo info = mListeners.get(i); if (info.listener == listener && info.userid == userid) { - mListeners.remove(listener); + mListeners.remove(i); + if (info.connection != null) { + mContext.unbindService(info.connection); + } + } + } + } + } + + /** + * Remove a listener service for the given user by ComponentName + */ + private void unregisterListenerService(ComponentName name, int userid) { + checkCallerIsSystem(); + + synchronized (mNotificationList) { + final int N = mListeners.size(); + for (int i=N-1; i>=0; i--) { + final NotificationListenerInfo info = mListeners.get(i); + if (name.equals(info.component) + && info.userid == userid) { + mListeners.remove(i); + if (info.connection != null) { + mContext.unbindService(info.connection); + } } } } } + /** + * asynchronously notify all listeners about a new notification + */ private void notifyPostedLocked(NotificationRecord n) { final StatusBarNotification sbn = n.sbn; for (final NotificationListenerInfo info : mListeners) { @@ -530,6 +733,9 @@ public class NotificationManagerService extends INotificationManager.Stub } } + /** + * asynchronously notify all listeners about a removed notification + */ private void notifyRemovedLocked(NotificationRecord n) { final StatusBarNotification sbn = n.sbn; for (final NotificationListenerInfo info : mListeners) { @@ -541,6 +747,57 @@ public class NotificationManagerService extends INotificationManager.Stub } } + // -- APIs to support listeners clicking/clearing notifications -- + + private NotificationListenerInfo checkListenerToken(INotificationListener listener) { + final IBinder token = listener.asBinder(); + final int N = mListeners.size(); + for (int i=0; i<N; i++) { + final NotificationListenerInfo info = mListeners.get(i); + if (info.listener.asBinder() == token) return info; + } + throw new SecurityException("Disallowed call from unknown listener: " + listener); + } + + /** + * Allow an INotificationListener to simulate a "clear all" operation. + * + * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} + * + * @param token The binder for the listener, to check that the caller is allowed + */ + public void clearAllNotificationsFromListener(INotificationListener token) { + NotificationListenerInfo info = checkListenerToken(token); + long identity = Binder.clearCallingIdentity(); + try { + cancelAll(info.userid); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** + * Allow an INotificationListener to simulate clearing (dismissing) a single notification. + * + * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} + * + * @param token The binder for the listener, to check that the caller is allowed + */ + public void clearNotificationFromListener(INotificationListener token, String pkg, String tag, int id) { + NotificationListenerInfo info = checkListenerToken(token); + long identity = Binder.clearCallingIdentity(); + try { + cancelNotification(pkg, tag, id, 0, + Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, + true, + info.userid); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + // -- end of listener APIs -- + public static final class NotificationRecord { final StatusBarNotification sbn; @@ -759,12 +1016,23 @@ public class NotificationManagerService extends INotificationManager.Stub } pkgList = new String[]{pkgName}; } + + boolean anyListenersInvolved = false; if (pkgList != null && (pkgList.length > 0)) { for (String pkgName : pkgList) { cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart, UserHandle.USER_ALL); + if (mEnabledListenerPackageNames.contains(pkgName)) { + anyListenersInvolved = true; + } } } + + if (anyListenersInvolved) { + // make sure we're still bound to any of our + // listeners who may have just upgraded + rebindListenerServices(); + } } else if (action.equals(Intent.ACTION_SCREEN_ON)) { // Keep track of screen on/off state, but do not turn off the notification light // until user passes through the lock screen or views the notification. @@ -795,7 +1063,7 @@ public class NotificationManagerService extends INotificationManager.Stub = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); private final Uri ENABLED_NOTIFICATION_LISTENERS_URI - = Settings.System.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); SettingsObserver(Handler handler) { super(handler); @@ -804,9 +1072,9 @@ public class NotificationManagerService extends INotificationManager.Stub void observe() { ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, - false, this); + false, this, UserHandle.USER_ALL); resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI, - false, this); + false, this, UserHandle.USER_ALL); update(null); } @@ -825,19 +1093,7 @@ public class NotificationManagerService extends INotificationManager.Stub } } if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) { - String pkglist = Settings.Secure.getString( - mContext.getContentResolver(), - Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); - mEnabledListenersForCurrentUser.clear(); - if (pkglist != null) { - String[] pkgs = pkglist.split(";"); - for (int i=0; i<pkgs.length; i++) { - final String pkg = pkgs[i]; - if (pkg != null && ! "".equals(pkg)) { - mEnabledListenersForCurrentUser.add(pkgs[i]); - } - } - } + rebindListenerServices(); } } } @@ -956,6 +1212,9 @@ public class NotificationManagerService extends INotificationManager.Stub // no beeping until we're basically done booting mSystemReady = true; + + // make sure our listener services are properly bound + rebindListenerServices(); } // Toasts @@ -1781,16 +2040,17 @@ public class NotificationManagerService extends INotificationManager.Stub pw.println("Current Notification Manager state:"); - pw.print(" Enabled listeners: ["); - for (String pkg : mEnabledListenersForCurrentUser) { - pw.print(" " + pkg); + pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size() + + ") enabled for current user:"); + for (ComponentName cmpt : mEnabledListenersForCurrentUser) { + pw.println(" " + cmpt); } - pw.println(" ]"); - pw.println(" Live listeners:"); + pw.println(" Live listeners (" + mListeners.size() + "):"); for (NotificationListenerInfo info : mListeners) { - pw.println(" " + info.pkg + " (user " + info.userid + "): " + info.listener - + (info.isSystem?" SYSTEM":"")); + pw.println(" " + info.component + + " (user " + info.userid + "): " + info.listener + + (info.isSystem?" SYSTEM":"")); } int N; diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java index 1fe98af..c21d8c6 100644 --- a/services/java/com/android/server/StatusBarManagerService.java +++ b/services/java/com/android/server/StatusBarManagerService.java @@ -17,6 +17,7 @@ package com.android.server; import android.app.StatusBarManager; +import android.service.notification.StatusBarNotification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -33,7 +34,6 @@ import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; -import com.android.internal.statusbar.StatusBarNotification; import com.android.server.wm.WindowManagerService; import java.io.FileDescriptor; diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java index ebbbd86..062183b 100644 --- a/services/java/com/android/server/firewall/IntentFirewall.java +++ b/services/java/com/android/server/firewall/IntentFirewall.java @@ -42,7 +42,9 @@ import java.util.List; public class IntentFirewall { private static final String TAG = "IntentFirewall"; - private static final String RULES_FILENAME = "ifw.xml"; + // e.g. /data/system/ifw/ifw.xml or /data/secure/system/ifw/ifw.xml + private static final File RULES_FILE = + new File(Environment.getSystemSecureDirectory(), "ifw/ifw.xml"); private static final String TAG_RULES = "rules"; private static final String TAG_ACTIVITY = "activity"; @@ -93,9 +95,7 @@ public class IntentFirewall { public IntentFirewall(AMSInterface ams) { mAms = ams; - File dataSystemDir = new File(Environment.getDataDirectory(), "system"); - File rulesFile = new File(dataSystemDir, RULES_FILENAME); - readRules(rulesFile); + readRules(getRulesFile()); } public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp, @@ -127,6 +127,10 @@ public class IntentFirewall { return !block; } + public static File getRulesFile() { + return RULES_FILE; + } + private void readRules(File rulesFile) { FileInputStream fis; try { diff --git a/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java b/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java new file mode 100644 index 0000000..9185903 --- /dev/null +++ b/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 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.updates; + +import com.android.server.firewall.IntentFirewall; + +public class IntentFirewallInstallReceiver extends ConfigUpdateInstallReceiver { + + public IntentFirewallInstallReceiver() { + super(IntentFirewall.getRulesFile().getParent(), IntentFirewall.getRulesFile().getName(), + "metadata/", "version"); + } +} diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index cbc42eb..af603fd 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -5365,6 +5365,9 @@ public class WindowManagerService extends IWindowManager.Stub if (maxLayer < winAnim.mSurfaceLayer) { maxLayer = winAnim.mSurfaceLayer; } + if (minLayer > winAnim.mSurfaceLayer) { + minLayer = winAnim.mSurfaceLayer; + } // Don't include wallpaper in bounds calculation if (!ws.mIsWallpaper) { @@ -5377,17 +5380,14 @@ public class WindowManagerService extends IWindowManager.Stub frame.union(left, top, right, bottom); } - if (ws.mAppToken != null && ws.mAppToken.token == appToken) { - if (minLayer > ws.mWinAnimator.mSurfaceLayer) { - minLayer = ws.mWinAnimator.mSurfaceLayer; - } - if (ws.isDisplayedLw()) { - screenshotReady = true; - } - if (fullscreen) { - // No point in continuing down through windows. - break; - } + if (ws.mAppToken != null && ws.mAppToken.token == appToken && + ws.isDisplayedLw()) { + screenshotReady = true; + } + + if (fullscreen) { + // No point in continuing down through windows. + break; } } @@ -5461,14 +5461,12 @@ public class WindowManagerService extends IWindowManager.Stub rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer); } } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES); - if (DEBUG_SCREENSHOT && retryCount > MAX_SCREENSHOT_RETRIES) { - Slog.i(TAG, "Screenshot max retries " + retryCount + " of " + appToken + " appWin=" - + (appWin == null ? "null" : (appWin + " drawState=" - + appWin.mWinAnimator.mDrawState))); - } + if (retryCount > MAX_SCREENSHOT_RETRIES) Slog.i(TAG, "Screenshot max retries " + + retryCount + " of " + appToken + " appWin=" + (appWin == null ? + "null" : (appWin + " drawState=" + appWin.mWinAnimator.mDrawState))); if (rawss == null) { - Slog.w(TAG, "Failure taking screenshot for (" + dw + "x" + dh + Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh + ") to layer " + maxLayer); return null; } @@ -5481,7 +5479,7 @@ public class WindowManagerService extends IWindowManager.Stub canvas.drawBitmap(rawss, matrix, null); canvas.setBitmap(null); - if (DEBUG_SCREENSHOT) { + if (true || DEBUG_SCREENSHOT) { // TEST IF IT's ALL BLACK int[] buffer = new int[bm.getWidth() * bm.getHeight()]; bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight()); @@ -5494,7 +5492,8 @@ public class WindowManagerService extends IWindowManager.Stub } if (allBlack) { Slog.i(TAG, "Screenshot " + appWin + " was all black! mSurfaceLayer=" + - (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null")); + (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null") + + " minLayer=" + minLayer + " maxLayer=" + maxLayer); } } diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java index 4345098..ba160b1 100644 --- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java +++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java @@ -33,13 +33,10 @@ import android.util.Log; import android.net.Uri; import android.os.SystemClock; import android.widget.RemoteViews; -import android.widget.TextView; -import android.widget.ProgressBar; import android.os.PowerManager; // private NM API import android.app.INotificationManager; -import com.android.internal.statusbar.StatusBarNotification; public class NotificationTestList extends TestActivity { |