diff options
author | Daniel Sandler <dsandler@android.com> | 2013-03-22 18:29:23 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-04-05 19:54:44 +0000 |
commit | 5feceebb892d4cb5777cea3c6174b206705d456b (patch) | |
tree | 58feee1605f35b07e70887f4ec017d80b5687436 | |
parent | bab9687e6473072d6ff4f7ea5a7b21bcfbf95744 (diff) | |
download | frameworks_base-5feceebb892d4cb5777cea3c6174b206705d456b.zip frameworks_base-5feceebb892d4cb5777cea3c6174b206705d456b.tar.gz frameworks_base-5feceebb892d4cb5777cea3c6174b206705d456b.tar.bz2 |
New NotificationListenerService.
This is the best and only way for apps to listen for
notifications: create a NotificationListenerService, wait
for the NoMan to bind to you (as a result of the user
checking a box somewhere in Settings and agreeing to a
scary dialog box), and you'll start receiving notification
posted and dismissed callbacks. Your service, while enabled,
will also be able to clear one or all notifications.
Use this power wisely.
This change moves StatusBarNotification out of
com.android.internal into android.service.notification.
[Internal customers, including System UI and early users of
the system-only listener binder API, will need to be
updated.]
Bug: 8199624
Change-Id: I1be46f823d4b3ddc901109ec1e085cd6deb740c2
26 files changed, 573 insertions, 93 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/provider/Settings.java b/core/java/android/provider/Settings.java index a0473a4..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 /** 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/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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ffceb68..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"/> 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/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/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/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 { |