ConditionProviderService.java revision 4fe98d6d4cc4898809b81d001f6b8a1b71b08f3a
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.app.INotificationManager;
21import android.app.Service;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.net.Uri;
26import android.os.Handler;
27import android.os.IBinder;
28import android.os.Message;
29import android.os.ServiceManager;
30import android.util.Log;
31
32/**
33 * A service that provides conditions about boolean state.
34 * <p>To extend this class, you must declare the service in your manifest file with
35 * the {@link android.Manifest.permission#BIND_CONDITION_PROVIDER_SERVICE} permission
36 * and include an intent filter with the {@link #SERVICE_INTERFACE} action. If you want users to be
37 * able to create and update conditions for this service to monitor, include the
38 * {@link #META_DATA_RULE_TYPE} and {@link #META_DATA_CONFIGURATION_ACTIVITY} tags. For example:</p>
39 * <pre>
40 * &lt;service android:name=".MyConditionProvider"
41 *          android:label="&#64;string/service_name"
42 *          android:permission="android.permission.BIND_CONDITION_PROVIDER_SERVICE">
43 *     &lt;intent-filter>
44 *         &lt;action android:name="android.service.notification.ConditionProviderService" />
45 *     &lt;/intent-filter>
46 *     &lt;meta-data
47 *               android:name="android.service.zen.automatic.ruleType"
48 *               android:value="@string/my_condition_rule">
49 *           &lt;/meta-data>
50 *           &lt;meta-data
51 *               android:name="android.service.zen.automatic.configurationActivity"
52 *               android:value="com.my.package/.MyConditionConfigurationActivity">
53 *           &lt;/meta-data>
54 * &lt;/service></pre>
55 *
56 */
57public abstract class ConditionProviderService extends Service {
58    private final String TAG = ConditionProviderService.class.getSimpleName()
59            + "[" + getClass().getSimpleName() + "]";
60
61    private final H mHandler = new H();
62
63    private Provider mProvider;
64    private INotificationManager mNoMan;
65
66    /**
67     * The {@link Intent} that must be declared as handled by the service.
68     */
69    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
70    public static final String SERVICE_INTERFACE
71            = "android.service.notification.ConditionProviderService";
72
73    /**
74     * The name of the {@code meta-data} tag containing a localized name of the type of zen rules
75     * provided by this service.
76     */
77    public static final String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
78
79    /**
80     * The name of the {@code meta-data} tag containing the {@link ComponentName} of an activity
81     * that allows users to configure the conditions provided by this service.
82     */
83    public static final String META_DATA_CONFIGURATION_ACTIVITY =
84            "android.service.zen.automatic.configurationActivity";
85
86    /**
87     * A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}.
88     */
89    public static final String EXTRA_RULE_ID = "android.content.automatic.ruleId";
90
91    /**
92     * Called when this service is connected.
93     */
94    abstract public void onConnected();
95
96    /**
97     * Called when the system wants to know the state of Conditions managed by this provider.
98     *
99     * Implementations should evaluate the state of all subscribed conditions, and provide updates
100     * by calling {@link #notifyCondition(Condition)} or {@link #notifyConditions(Condition...)}.
101     * @param relevance
102     */
103    abstract public void onRequestConditions(int relevance);
104
105    /**
106     * Called by the system when there is a new {@link Condition} to be managed by this provider.
107     * @param conditionId the Uri describing the criteria of the condition.
108     */
109    abstract public void onSubscribe(Uri conditionId);
110
111    /**
112     * Called by the system when a {@link Condition} has been deleted.
113     * @param conditionId the Uri describing the criteria of the deleted condition.
114     */
115    abstract public void onUnsubscribe(Uri conditionId);
116
117    private final INotificationManager getNotificationInterface() {
118        if (mNoMan == null) {
119            mNoMan = INotificationManager.Stub.asInterface(
120                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
121        }
122        return mNoMan;
123    }
124
125    /**
126     * Informs the notification manager that the state of a Condition has changed.
127     * @param condition the condition that has changed.
128     */
129    public final void notifyCondition(Condition condition) {
130        if (condition == null) return;
131        notifyConditions(new Condition[]{ condition });
132    }
133
134    /**
135     * Informs the notification manager that the state of one or more Conditions has changed.
136     * @param conditions the changed conditions.
137     */
138    public final void notifyConditions(Condition... conditions) {
139        if (!isBound() || conditions == null) return;
140        try {
141            getNotificationInterface().notifyConditions(getPackageName(), mProvider, conditions);
142        } catch (android.os.RemoteException ex) {
143            Log.v(TAG, "Unable to contact notification manager", ex);
144        }
145    }
146
147    @Override
148    public IBinder onBind(Intent intent) {
149        if (mProvider == null) {
150            mProvider = new Provider();
151        }
152        return mProvider;
153    }
154
155    private boolean isBound() {
156        if (mProvider == null) {
157            Log.w(TAG, "Condition provider service not yet bound.");
158            return false;
159        }
160        return true;
161    }
162
163    private final class Provider extends IConditionProvider.Stub {
164        @Override
165        public void onConnected() {
166            mHandler.obtainMessage(H.ON_CONNECTED).sendToTarget();
167        }
168
169        @Override
170        public void onRequestConditions(int relevance) {
171            mHandler.obtainMessage(H.ON_REQUEST_CONDITIONS, relevance, 0).sendToTarget();
172        }
173
174        @Override
175        public void onSubscribe(Uri conditionId) {
176            mHandler.obtainMessage(H.ON_SUBSCRIBE, conditionId).sendToTarget();
177        }
178
179        @Override
180        public void onUnsubscribe(Uri conditionId) {
181            mHandler.obtainMessage(H.ON_UNSUBSCRIBE, conditionId).sendToTarget();
182        }
183    }
184
185    private final class H extends Handler {
186        private static final int ON_CONNECTED = 1;
187        private static final int ON_REQUEST_CONDITIONS = 2;
188        private static final int ON_SUBSCRIBE = 3;
189        private static final int ON_UNSUBSCRIBE = 4;
190
191        @Override
192        public void handleMessage(Message msg) {
193            String name = null;
194            try {
195                switch(msg.what) {
196                    case ON_CONNECTED:
197                        name = "onConnected";
198                        onConnected();
199                        break;
200                    case ON_REQUEST_CONDITIONS:
201                        name = "onRequestConditions";
202                        onRequestConditions(msg.arg1);
203                        break;
204                    case ON_SUBSCRIBE:
205                        name = "onSubscribe";
206                        onSubscribe((Uri)msg.obj);
207                        break;
208                    case ON_UNSUBSCRIBE:
209                        name = "onUnsubscribe";
210                        onUnsubscribe((Uri)msg.obj);
211                        break;
212                }
213            } catch (Throwable t) {
214                Log.w(TAG, "Error running " + name, t);
215            }
216        }
217    }
218}
219