• 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 com.android.server.notification;
18 
19 import android.app.AlarmManager;
20 import android.app.PendingIntent;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.net.Uri;
26 import android.os.UserHandle;
27 import android.service.notification.Condition;
28 import android.service.notification.ZenModeConfig;
29 import android.text.format.DateUtils;
30 import android.util.Log;
31 import android.util.Slog;
32 
33 import com.android.server.notification.NotificationManagerService.DumpFilter;
34 import com.android.server.pm.PackageManagerService;
35 
36 import java.io.PrintWriter;
37 
38 /** Built-in zen condition provider for simple time-based conditions */
39 public class CountdownConditionProvider extends SystemConditionProviderService {
40     private static final String TAG = "ConditionProviders.CCP";
41     private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
42 
43     private static final String ACTION = CountdownConditionProvider.class.getName();
44     private static final int REQUEST_CODE = 100;
45     private static final String EXTRA_CONDITION_ID = "condition_id";
46 
47     private final Context mContext = this;
48     private final Receiver mReceiver = new Receiver();
49 
50     private boolean mConnected;
51     private long mTime;
52     private boolean mIsAlarm;
53 
CountdownConditionProvider()54     public CountdownConditionProvider() {
55         if (DEBUG) Slog.d(TAG, "new CountdownConditionProvider()");
56     }
57 
58     @Override
isValidConditionId(Uri id)59     public boolean isValidConditionId(Uri id) {
60         return ZenModeConfig.isValidCountdownConditionId(id);
61     }
62 
63     @Override
onBootComplete()64     public void onBootComplete() {
65         // noop
66     }
67 
68     @Override
onUserSwitched(UserHandle user)69     public void onUserSwitched(UserHandle user) {
70         // Nothing to do because countdown conditions are not tied to any user data.
71     }
72 
73     @Override
dump(PrintWriter pw, DumpFilter filter)74     public void dump(PrintWriter pw, DumpFilter filter) {
75         pw.println("    CountdownConditionProvider:");
76         pw.print("      mConnected="); pw.println(mConnected);
77         pw.print("      mTime="); pw.println(mTime);
78     }
79 
80     @Override
onConnected()81     public void onConnected() {
82         if (DEBUG) Slog.d(TAG, "onConnected");
83         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION),
84                 Context.RECEIVER_EXPORTED_UNAUDITED);
85         mConnected = true;
86     }
87 
88     @Override
onDestroy()89     public void onDestroy() {
90         super.onDestroy();
91         if (DEBUG) Slog.d(TAG, "onDestroy");
92         if (mConnected) {
93             mContext.unregisterReceiver(mReceiver);
94         }
95         mConnected = false;
96     }
97 
98     @Override
onSubscribe(Uri conditionId)99     public void onSubscribe(Uri conditionId) {
100         if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
101         mTime = ZenModeConfig.tryParseCountdownConditionId(conditionId);
102         mIsAlarm = ZenModeConfig.isValidCountdownToAlarmConditionId(conditionId);
103         final AlarmManager alarms = (AlarmManager)
104                 mContext.getSystemService(Context.ALARM_SERVICE);
105         final PendingIntent pendingIntent = getPendingIntent(conditionId);
106         alarms.cancel(pendingIntent);
107         if (mTime > 0) {
108             final long now = System.currentTimeMillis();
109             final CharSequence span =
110                     DateUtils.getRelativeTimeSpanString(mTime, now, DateUtils.MINUTE_IN_MILLIS);
111             if (mTime <= now) {
112                 // in the past, already false
113                 notifyCondition(newCondition(mTime, mIsAlarm, Condition.STATE_FALSE));
114             } else {
115                 // in the future, set an alarm
116                 alarms.setExact(AlarmManager.RTC_WAKEUP, mTime, pendingIntent);
117             }
118             if (DEBUG) Slog.d(TAG, String.format(
119                     "%s %s for %s, %s in the future (%s), now=%s",
120                     (mTime <= now ? "Not scheduling" : "Scheduling"),
121                     ACTION, ts(mTime), mTime - now, span, ts(now)));
122         }
123     }
124 
getPendingIntent(Uri conditionId)125     PendingIntent getPendingIntent(Uri conditionId) {
126         final Intent intent = new Intent(ACTION)
127                 .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
128                 .putExtra(EXTRA_CONDITION_ID, conditionId)
129                 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
130         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
131                 intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
132         return pendingIntent;
133     }
134 
135     @Override
onUnsubscribe(Uri conditionId)136     public void onUnsubscribe(Uri conditionId) {
137         // noop
138     }
139 
140     private final class Receiver extends BroadcastReceiver {
141         @Override
onReceive(Context context, Intent intent)142         public void onReceive(Context context, Intent intent) {
143             if (ACTION.equals(intent.getAction())) {
144                 final Uri conditionId = intent.getParcelableExtra(EXTRA_CONDITION_ID, android.net.Uri.class);
145                 final boolean alarm = ZenModeConfig.isValidCountdownToAlarmConditionId(conditionId);
146                 final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
147                 if (DEBUG) Slog.d(TAG, "Countdown condition fired: " + conditionId);
148                 if (time > 0) {
149                     notifyCondition(newCondition(time, alarm, Condition.STATE_FALSE));
150                 }
151             }
152         }
153     }
154 
newCondition(long time, boolean alarm, int state)155     private static final Condition newCondition(long time, boolean alarm, int state) {
156         return new Condition(ZenModeConfig.toCountdownConditionId(time, alarm),
157                 "", "", "", 0, state,Condition.FLAG_RELEVANT_NOW);
158     }
159 
tryParseDescription(Uri conditionUri)160     public static String tryParseDescription(Uri conditionUri) {
161         final long time = ZenModeConfig.tryParseCountdownConditionId(conditionUri);
162         if (time == 0) return null;
163         final long now = System.currentTimeMillis();
164         final CharSequence span =
165                 DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS);
166         return String.format("Scheduled for %s, %s in the future (%s), now=%s",
167                 ts(time), time - now, span, ts(now));
168     }
169 
170 }
171