• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.service.notification;
18 
19 import android.annotation.SdkConstant;
20 import android.annotation.SystemApi;
21 import android.app.INotificationManager;
22 import android.app.Service;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.net.Uri;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.Message;
30 import android.os.ServiceManager;
31 import android.util.Log;
32 
33 /**
34  * A service that provides conditions about boolean state.
35  * <p>To extend this class, you must declare the service in your manifest file with
36  * the {@link android.Manifest.permission#BIND_CONDITION_PROVIDER_SERVICE} permission
37  * and include an intent filter with the {@link #SERVICE_INTERFACE} action. If you want users to be
38  * able to create and update conditions for this service to monitor, include the
39  * {@link #META_DATA_RULE_TYPE} and {@link #META_DATA_CONFIGURATION_ACTIVITY} tags and request the
40  * {@link android.Manifest.permission#ACCESS_NOTIFICATION_POLICY} permission. For example:</p>
41  * <pre>
42  * &lt;service android:name=".MyConditionProvider"
43  *          android:label="&#64;string/service_name"
44  *          android:permission="android.permission.BIND_CONDITION_PROVIDER_SERVICE">
45  *     &lt;intent-filter>
46  *         &lt;action android:name="android.service.notification.ConditionProviderService" />
47  *     &lt;/intent-filter>
48  *     &lt;meta-data
49  *               android:name="android.service.zen.automatic.ruleType"
50  *               android:value="@string/my_condition_rule">
51  *           &lt;/meta-data>
52  *           &lt;meta-data
53  *               android:name="android.service.zen.automatic.configurationActivity"
54  *               android:value="com.my.package/.MyConditionConfigurationActivity">
55  *           &lt;/meta-data>
56  * &lt;/service></pre>
57  *
58  */
59 public abstract class ConditionProviderService extends Service {
60     private final String TAG = ConditionProviderService.class.getSimpleName()
61             + "[" + getClass().getSimpleName() + "]";
62 
63     private final H mHandler = new H();
64 
65     private Provider mProvider;
66     private INotificationManager mNoMan;
67 
68     /**
69      * The {@link Intent} that must be declared as handled by the service.
70      */
71     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
72     public static final String SERVICE_INTERFACE
73             = "android.service.notification.ConditionProviderService";
74 
75     /**
76      * The name of the {@code meta-data} tag containing a localized name of the type of zen rules
77      * provided by this service.
78      */
79     public static final String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
80 
81     /**
82      * The name of the {@code meta-data} tag containing the {@link ComponentName} of an activity
83      * that allows users to configure the conditions provided by this service.
84      */
85     public static final String META_DATA_CONFIGURATION_ACTIVITY =
86             "android.service.zen.automatic.configurationActivity";
87 
88     /**
89      * The name of the {@code meta-data} tag containing the maximum number of rule instances that
90      * can be created for this rule type. Omit or enter a value <= 0 to allow unlimited instances.
91      */
92     public static final String META_DATA_RULE_INSTANCE_LIMIT =
93             "android.service.zen.automatic.ruleInstanceLimit";
94 
95     /**
96      * A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}.
97      */
98     public static final String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
99 
100     /**
101      * Called when this service is connected.
102      */
onConnected()103     abstract public void onConnected();
104 
105     @SystemApi
onRequestConditions(int relevance)106     public void onRequestConditions(int relevance) {}
107 
108     /**
109      * Called by the system when there is a new {@link Condition} to be managed by this provider.
110      * @param conditionId the Uri describing the criteria of the condition.
111      */
onSubscribe(Uri conditionId)112     abstract public void onSubscribe(Uri conditionId);
113 
114     /**
115      * Called by the system when a {@link Condition} has been deleted.
116      * @param conditionId the Uri describing the criteria of the deleted condition.
117      */
onUnsubscribe(Uri conditionId)118     abstract public void onUnsubscribe(Uri conditionId);
119 
getNotificationInterface()120     private final INotificationManager getNotificationInterface() {
121         if (mNoMan == null) {
122             mNoMan = INotificationManager.Stub.asInterface(
123                     ServiceManager.getService(Context.NOTIFICATION_SERVICE));
124         }
125         return mNoMan;
126     }
127 
128     /**
129      * Informs the notification manager that the state of a Condition has changed. Use this method
130      * to put the system into Do Not Disturb mode or request that it exits Do Not Disturb mode. This
131      * call will be ignored unless there is an enabled {@link android.app.AutomaticZenRule} owned by
132      * service that has an {@link android.app.AutomaticZenRule#getConditionId()} equal to this
133      * {@link Condition#id}.
134      * @param condition the condition that has changed.
135      */
notifyCondition(Condition condition)136     public final void notifyCondition(Condition condition) {
137         if (condition == null) return;
138         notifyConditions(new Condition[]{ condition });
139     }
140 
141     /**
142      * Informs the notification manager that the state of one or more Conditions has changed. See
143      * {@link #notifyCondition(Condition)} for restrictions.
144      * @param conditions the changed conditions.
145      */
notifyConditions(Condition... conditions)146     public final void notifyConditions(Condition... conditions) {
147         if (!isBound() || conditions == null) return;
148         try {
149             getNotificationInterface().notifyConditions(getPackageName(), mProvider, conditions);
150         } catch (android.os.RemoteException ex) {
151             Log.v(TAG, "Unable to contact notification manager", ex);
152         }
153     }
154 
155     @Override
onBind(Intent intent)156     public IBinder onBind(Intent intent) {
157         if (mProvider == null) {
158             mProvider = new Provider();
159         }
160         return mProvider;
161     }
162 
isBound()163     private boolean isBound() {
164         if (mProvider == null) {
165             Log.w(TAG, "Condition provider service not yet bound.");
166             return false;
167         }
168         return true;
169     }
170 
171     private final class Provider extends IConditionProvider.Stub {
172         @Override
onConnected()173         public void onConnected() {
174             mHandler.obtainMessage(H.ON_CONNECTED).sendToTarget();
175         }
176 
177         @Override
onSubscribe(Uri conditionId)178         public void onSubscribe(Uri conditionId) {
179             mHandler.obtainMessage(H.ON_SUBSCRIBE, conditionId).sendToTarget();
180         }
181 
182         @Override
onUnsubscribe(Uri conditionId)183         public void onUnsubscribe(Uri conditionId) {
184             mHandler.obtainMessage(H.ON_UNSUBSCRIBE, conditionId).sendToTarget();
185         }
186     }
187 
188     private final class H extends Handler {
189         private static final int ON_CONNECTED = 1;
190         private static final int ON_SUBSCRIBE = 3;
191         private static final int ON_UNSUBSCRIBE = 4;
192 
193         @Override
handleMessage(Message msg)194         public void handleMessage(Message msg) {
195             String name = null;
196             try {
197                 switch(msg.what) {
198                     case ON_CONNECTED:
199                         name = "onConnected";
200                         onConnected();
201                         break;
202                     case ON_SUBSCRIBE:
203                         name = "onSubscribe";
204                         onSubscribe((Uri)msg.obj);
205                         break;
206                     case ON_UNSUBSCRIBE:
207                         name = "onUnsubscribe";
208                         onUnsubscribe((Uri)msg.obj);
209                         break;
210                 }
211             } catch (Throwable t) {
212                 Log.w(TAG, "Error running " + name, t);
213             }
214         }
215     }
216 }
217