blob: 5567944169a39e60c862d847cbfe4112c2b6d2b6 [file] [log] [blame]
/**
* Copyright (c) 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.notification;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.R;
import libcore.util.Objects;
import java.io.PrintWriter;
import java.util.Arrays;
public class ConditionProviders extends ManagedServices {
private final ZenModeHelper mZenModeHelper;
private final ArrayMap<IBinder, IConditionListener> mListeners
= new ArrayMap<IBinder, IConditionListener>();
private final ArrayMap<Uri, ManagedServiceInfo> mConditions
= new ArrayMap<Uri, ManagedServiceInfo>();
private Uri mCurrentConditionId;
public ConditionProviders(Context context, Handler handler,
UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
super(context, handler, new Object(), userProfiles);
mZenModeHelper = zenModeHelper;
mZenModeHelper.addCallback(new ZenModeHelperCallback());
}
@Override
protected Config getConfig() {
Config c = new Config();
c.caption = "condition provider";
c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
c.clientLabel = R.string.condition_provider_service_binding_label;
return c;
}
@Override
public void dump(PrintWriter pw) {
super.dump(pw);
synchronized(mMutex) {
pw.print(" mCurrentConditionId="); pw.println(mCurrentConditionId);
pw.print(" mListeners("); pw.print(mListeners.size()); pw.println("):");
for (int i = 0; i < mListeners.size(); i++) {
pw.print(" "); pw.println(mListeners.keyAt(i));
}
pw.print(" mConditions("); pw.print(mConditions.size()); pw.println("):");
for (int i = 0; i < mConditions.size(); i++) {
pw.print(" "); pw.print(mConditions.keyAt(i));
final ManagedServiceInfo info = mConditions.valueAt(i);
pw.print(" -> "); pw.print(info.component);
if (!mServices.contains(info)) {
pw.print(" (orphan)");
}
pw.println();
}
}
}
@Override
protected IInterface asInterface(IBinder binder) {
return IConditionProvider.Stub.asInterface(binder);
}
@Override
protected void onServiceAdded(IInterface service) {
Slog.d(TAG, "onServiceAdded " + service);
final IConditionProvider provider = (IConditionProvider) service;
try {
provider.onConnected();
} catch (RemoteException e) {
// we tried
}
}
@Override
protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
if (removed == null) return;
if (mCurrentConditionId != null) {
if (removed.equals(mConditions.get(mCurrentConditionId))) {
mCurrentConditionId = null;
mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
}
}
for (int i = mConditions.size() - 1; i >= 0; i--) {
if (removed.equals(mConditions.valueAt(i))) {
mConditions.removeAt(i);
}
}
}
public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
synchronized(mMutex) {
return checkServiceTokenLocked(provider);
}
}
public void requestZenModeConditions(IConditionListener callback, boolean requested) {
synchronized(mMutex) {
if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
+ " requested=" + requested);
if (callback == null) return;
if (requested) {
mListeners.put(callback.asBinder(), callback);
requestConditionsLocked(Condition.FLAG_RELEVANT_NOW);
} else {
mListeners.remove(callback.asBinder());
if (mListeners.isEmpty()) {
requestConditionsLocked(0);
}
}
}
}
public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
synchronized(mMutex) {
if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
+ (conditions == null ? null : Arrays.asList(conditions)));
if (conditions == null || conditions.length == 0) return;
final int N = conditions.length;
boolean valid = true;
for (int i = 0; i < N; i++) {
final Uri id = conditions[i].id;
if (!Condition.isValidId(id, pkg)) {
Slog.w(TAG, "Ignoring conditions from " + pkg + " for invalid id: " + id);
valid = false;
}
}
if (!valid) return;
for (int i = 0; i < N; i++) {
mConditions.put(conditions[i].id, info);
}
for (IConditionListener listener : mListeners.values()) {
try {
listener.onConditionsReceived(conditions);
} catch (RemoteException e) {
Slog.w(TAG, "Error sending conditions to listener " + listener, e);
}
}
if (mCurrentConditionId != null) {
for (int i = 0; i < N; i++) {
final Condition c = conditions[i];
if (!c.id.equals(mCurrentConditionId)) continue;
if (c.state == Condition.STATE_TRUE || c.state == Condition.STATE_ERROR) {
triggerExitLocked(c.state == Condition.STATE_ERROR);
return;
}
}
}
}
}
private void triggerExitLocked(boolean error) {
if (error) {
Slog.w(TAG, "Zen mode exit condition failed");
} else if (DEBUG) {
Slog.d(TAG, "Zen mode exit condition triggered");
}
mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
unsubscribeLocked(mCurrentConditionId);
mCurrentConditionId = null;
}
public void setZenModeCondition(Uri conditionId) {
synchronized(mMutex) {
if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
if (Objects.equal(mCurrentConditionId, conditionId)) return;
if (mCurrentConditionId != null) {
unsubscribeLocked(mCurrentConditionId);
}
if (conditionId != null) {
final ManagedServiceInfo info = mConditions.get(conditionId);
final IConditionProvider provider = provider(info);
if (provider == null) return;
try {
provider.onSubscribe(conditionId);
} catch (RemoteException e) {
Slog.w(TAG, "Error subscribing to " + conditionId
+ " from " + info.component, e);
}
}
mCurrentConditionId = conditionId;
}
}
private void unsubscribeLocked(Uri conditionId) {
final ManagedServiceInfo info = mConditions.get(mCurrentConditionId);
final IConditionProvider provider = provider(info);
if (provider == null) return;
try {
provider.onUnsubscribe(conditionId);
} catch (RemoteException e) {
Slog.w(TAG, "Error unsubscribing to " + conditionId + " from " + info.component, e);
}
}
private static IConditionProvider provider(ManagedServiceInfo info) {
return info == null ? null : (IConditionProvider) info.service;
}
private void requestConditionsLocked(int flags) {
for (ManagedServiceInfo info : mServices) {
final IConditionProvider provider = provider(info);
if (provider == null) continue;
try {
provider.onRequestConditions(flags);
} catch (RemoteException e) {
Slog.w(TAG, "Error requesting conditions from " + info.component, e);
}
}
}
private class ZenModeHelperCallback extends ZenModeHelper.Callback {
@Override
void onZenModeChanged() {
final int mode = mZenModeHelper.getZenMode();
if (mode == Global.ZEN_MODE_OFF) {
synchronized (mMutex) {
if (mCurrentConditionId != null) {
if (DEBUG) Slog.d(TAG, "Zen mode off, forcing unsubscribe from "
+ mCurrentConditionId);
unsubscribeLocked(mCurrentConditionId);
mCurrentConditionId = null;
}
}
}
}
}
}