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.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. For example:</p>
37 * <pre>
38 * &lt;service android:name=".MyConditionProvider"
39 *          android:label="&#64;string/service_name"
40 *          android:permission="android.permission.BIND_CONDITION_PROVIDER_SERVICE">
41 *     &lt;intent-filter>
42 *         &lt;action android:name="android.service.notification.ConditionProviderService" />
43 *     &lt;/intent-filter>
44 * &lt;/service></pre>
45 *
46 * @hide
47 */
48@SystemApi
49public abstract class ConditionProviderService extends Service {
50    private final String TAG = ConditionProviderService.class.getSimpleName()
51            + "[" + getClass().getSimpleName() + "]";
52
53    private final H mHandler = new H();
54
55    private Provider mProvider;
56    private INotificationManager mNoMan;
57
58    /**
59     * The {@link Intent} that must be declared as handled by the service.
60     */
61    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
62    public static final String SERVICE_INTERFACE
63            = "android.service.notification.ConditionProviderService";
64
65    abstract public void onConnected();
66    abstract public void onRequestConditions(int relevance);
67    abstract public void onSubscribe(Uri conditionId);
68    abstract public void onUnsubscribe(Uri conditionId);
69
70    private final INotificationManager getNotificationInterface() {
71        if (mNoMan == null) {
72            mNoMan = INotificationManager.Stub.asInterface(
73                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
74        }
75        return mNoMan;
76    }
77
78    public final void notifyCondition(Condition condition) {
79        if (condition == null) return;
80        notifyConditions(new Condition[]{ condition });
81    }
82
83    public final void notifyConditions(Condition... conditions) {
84        if (!isBound() || conditions == null) return;
85        try {
86            getNotificationInterface().notifyConditions(getPackageName(), mProvider, conditions);
87        } catch (android.os.RemoteException ex) {
88            Log.v(TAG, "Unable to contact notification manager", ex);
89        }
90    }
91
92    @Override
93    public IBinder onBind(Intent intent) {
94        if (mProvider == null) {
95            mProvider = new Provider();
96        }
97        return mProvider;
98    }
99
100    private boolean isBound() {
101        if (mProvider == null) {
102            Log.w(TAG, "Condition provider service not yet bound.");
103            return false;
104        }
105        return true;
106    }
107
108    private final class Provider extends IConditionProvider.Stub {
109        @Override
110        public void onConnected() {
111            mHandler.obtainMessage(H.ON_CONNECTED).sendToTarget();
112        }
113
114        @Override
115        public void onRequestConditions(int relevance) {
116            mHandler.obtainMessage(H.ON_REQUEST_CONDITIONS, relevance, 0).sendToTarget();
117        }
118
119        @Override
120        public void onSubscribe(Uri conditionId) {
121            mHandler.obtainMessage(H.ON_SUBSCRIBE, conditionId).sendToTarget();
122        }
123
124        @Override
125        public void onUnsubscribe(Uri conditionId) {
126            mHandler.obtainMessage(H.ON_UNSUBSCRIBE, conditionId).sendToTarget();
127        }
128    }
129
130    private final class H extends Handler {
131        private static final int ON_CONNECTED = 1;
132        private static final int ON_REQUEST_CONDITIONS = 2;
133        private static final int ON_SUBSCRIBE = 3;
134        private static final int ON_UNSUBSCRIBE = 4;
135
136        @Override
137        public void handleMessage(Message msg) {
138            String name = null;
139            try {
140                switch(msg.what) {
141                    case ON_CONNECTED:
142                        name = "onConnected";
143                        onConnected();
144                        break;
145                    case ON_REQUEST_CONDITIONS:
146                        name = "onRequestConditions";
147                        onRequestConditions(msg.arg1);
148                        break;
149                    case ON_SUBSCRIBE:
150                        name = "onSubscribe";
151                        onSubscribe((Uri)msg.obj);
152                        break;
153                    case ON_UNSUBSCRIBE:
154                        name = "onUnsubscribe";
155                        onUnsubscribe((Uri)msg.obj);
156                        break;
157                }
158            } catch (Throwable t) {
159                Log.w(TAG, "Error running " + name, t);
160            }
161        }
162    }
163}
164