Commit 5feceebb authored by Daniel Sandler's avatar Daniel Sandler Committed by Android (Google) Code Review

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
parent bab9687e
......@@ -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 \
......
......@@ -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
# ************************************************
......@@ -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 {
......@@ -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
......@@ -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
/**
......
......@@ -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
......
/*
* 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);
}
}
}
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.internal.statusbar;
package android.service.notification;
parcelable StatusBarNotification;
......@@ -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);
......
......@@ -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
......
......@@ -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
......
......@@ -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"/>
......
......@@ -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>
......
......@@ -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" />
......
......@@ -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;
......
......@@ -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
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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";
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment