ConditionProviderService.java revision a62496d8f7cb9048331451af07466b1edc568c7d
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
17package android.service.notification;
18
19import android.annotation.SdkConstant;
20import android.annotation.SystemApi;
21import android.app.INotificationManager;
22import android.app.Service;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.net.Uri;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.Message;
30import android.os.ServiceManager;
31import 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 */
59public 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.content.automatic.ruleId";
99
100    /**
101     * Called when this service is connected.
102     */
103    abstract public void onConnected();
104
105    /**
106     * @removed
107     */
108    @SystemApi
109    public void onRequestConditions(int relevance) {}
110
111    /**
112     * Called by the system when there is a new {@link Condition} to be managed by this provider.
113     * @param conditionId the Uri describing the criteria of the condition.
114     */
115    abstract public void onSubscribe(Uri conditionId);
116
117    /**
118     * Called by the system when a {@link Condition} has been deleted.
119     * @param conditionId the Uri describing the criteria of the deleted condition.
120     */
121    abstract public void onUnsubscribe(Uri conditionId);
122
123    private final INotificationManager getNotificationInterface() {
124        if (mNoMan == null) {
125            mNoMan = INotificationManager.Stub.asInterface(
126                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
127        }
128        return mNoMan;
129    }
130
131    /**
132     * Informs the notification manager that the state of a Condition has changed. Use this method
133     * to put the system into Do Not Disturb mode or request that it exits Do Not Disturb mode. This
134     * call will be ignored unless there is an enabled {@link android.app.AutomaticZenRule} owned by
135     * service that has an {@link android.app.AutomaticZenRule#getConditionId()} equal to this
136     * {@link Condition#id}.
137     * @param condition the condition that has changed.
138     */
139    public final void notifyCondition(Condition condition) {
140        if (condition == null) return;
141        notifyConditions(new Condition[]{ condition });
142    }
143
144    /**
145     * Informs the notification manager that the state of one or more Conditions has changed. See
146     * {@link #notifyCondition(Condition)} for restrictions.
147     * @param conditions the changed conditions.
148     */
149    public final void notifyConditions(Condition... conditions) {
150        if (!isBound() || conditions == null) return;
151        try {
152            getNotificationInterface().notifyConditions(getPackageName(), mProvider, conditions);
153        } catch (android.os.RemoteException ex) {
154            Log.v(TAG, "Unable to contact notification manager", ex);
155        }
156    }
157
158    @Override
159    public IBinder onBind(Intent intent) {
160        if (mProvider == null) {
161            mProvider = new Provider();
162        }
163        return mProvider;
164    }
165
166    private boolean isBound() {
167        if (mProvider == null) {
168            Log.w(TAG, "Condition provider service not yet bound.");
169            return false;
170        }
171        return true;
172    }
173
174    private final class Provider extends IConditionProvider.Stub {
175        @Override
176        public void onConnected() {
177            mHandler.obtainMessage(H.ON_CONNECTED).sendToTarget();
178        }
179
180        @Override
181        public void onSubscribe(Uri conditionId) {
182            mHandler.obtainMessage(H.ON_SUBSCRIBE, conditionId).sendToTarget();
183        }
184
185        @Override
186        public void onUnsubscribe(Uri conditionId) {
187            mHandler.obtainMessage(H.ON_UNSUBSCRIBE, conditionId).sendToTarget();
188        }
189    }
190
191    private final class H extends Handler {
192        private static final int ON_CONNECTED = 1;
193        private static final int ON_SUBSCRIBE = 3;
194        private static final int ON_UNSUBSCRIBE = 4;
195
196        @Override
197        public void handleMessage(Message msg) {
198            String name = null;
199            try {
200                switch(msg.what) {
201                    case ON_CONNECTED:
202                        name = "onConnected";
203                        onConnected();
204                        break;
205                    case ON_SUBSCRIBE:
206                        name = "onSubscribe";
207                        onSubscribe((Uri)msg.obj);
208                        break;
209                    case ON_UNSUBSCRIBE:
210                        name = "onUnsubscribe";
211                        onUnsubscribe((Uri)msg.obj);
212                        break;
213                }
214            } catch (Throwable t) {
215                Log.w(TAG, "Error running " + name, t);
216            }
217        }
218    }
219}
220