blob: fa1a12030240d007b5ff05a44653965abbe6914e [file] [log] [blame]
Daichi Hirono2efe4ca2015-07-27 16:47:46 +09001/*
2 * Copyright (C) 2015 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 com.android.mtp;
18
Daichi Hirono203be492017-04-03 12:20:42 +090019import android.annotation.NonNull;
20import android.annotation.Nullable;
Daichi Hironoa57d9ed2015-12-07 10:52:42 +090021import android.app.Notification;
Daichi Hirono2efe4ca2015-07-27 16:47:46 +090022import android.app.Service;
Daichi Hironoa57d9ed2015-12-07 10:52:42 +090023import android.app.NotificationManager;
Daichi Hirono2efe4ca2015-07-27 16:47:46 +090024import android.content.Intent;
Daichi Hirono2efe4ca2015-07-27 16:47:46 +090025import android.os.IBinder;
Daichi Hirono203be492017-04-03 12:20:42 +090026import android.os.Parcelable;
Daichi Hironod3c6dd12017-01-26 14:40:19 +090027import android.service.notification.StatusBarNotification;
Daichi Hirono203be492017-04-03 12:20:42 +090028import android.util.Log;
29import com.android.internal.util.Preconditions;
Daichi Hironod3c6dd12017-01-26 14:40:19 +090030import java.util.HashSet;
31import java.util.Set;
Daichi Hirono2efe4ca2015-07-27 16:47:46 +090032
33/**
34 * Service to manage lifetime of DocumentsProvider's process.
35 * The service prevents the system from killing the process that holds USB connections. The service
36 * starts to run when the first MTP device is opened, and stops when the last MTP device is closed.
37 */
38public class MtpDocumentsService extends Service {
Daichi Hironofda74742016-02-01 13:00:31 +090039 static final String ACTION_UPDATE_NOTIFICATION = "com.android.mtp.UPDATE_NOTIFICATION";
Daichi Hirono203be492017-04-03 12:20:42 +090040 static final String EXTRA_DEVICE_IDS = "deviceIds";
41 static final String EXTRA_DEVICE_NOTIFICATIONS = "deviceNotifications";
Daichi Hirono2efe4ca2015-07-27 16:47:46 +090042
Daichi Hironod3c6dd12017-01-26 14:40:19 +090043 private NotificationManager mNotificationManager;
Daichi Hirono2efe4ca2015-07-27 16:47:46 +090044
45 @Override
46 public IBinder onBind(Intent intent) {
47 // The service is used via intents.
48 return null;
49 }
50
51 @Override
52 public void onCreate() {
53 super.onCreate();
Daichi Hironoa57d9ed2015-12-07 10:52:42 +090054 mNotificationManager = getSystemService(NotificationManager.class);
Daichi Hirono2efe4ca2015-07-27 16:47:46 +090055 }
56
57 @Override
58 public int onStartCommand(Intent intent, int flags, int startId) {
Daichi Hironoe0282dd2015-11-26 15:20:08 +090059 // If intent is null, the service was restarted.
Daichi Hironofda74742016-02-01 13:00:31 +090060 if (intent == null || ACTION_UPDATE_NOTIFICATION.equals(intent.getAction())) {
Daichi Hirono203be492017-04-03 12:20:42 +090061 final int[] ids = intent.hasExtra(EXTRA_DEVICE_IDS) ?
62 intent.getExtras().getIntArray(EXTRA_DEVICE_IDS) : null;
63 final Notification[] notifications = intent.hasExtra(EXTRA_DEVICE_NOTIFICATIONS) ?
64 castToNotifications(intent.getExtras().getParcelableArray(
65 EXTRA_DEVICE_NOTIFICATIONS)) : null;
66 return updateForegroundState(ids, notifications) ? START_STICKY : START_NOT_STICKY;
Daichi Hirono2efe4ca2015-07-27 16:47:46 +090067 }
Daichi Hironofda74742016-02-01 13:00:31 +090068 return START_NOT_STICKY;
Daichi Hirono2efe4ca2015-07-27 16:47:46 +090069 }
70
Daichi Hironoa57d9ed2015-12-07 10:52:42 +090071 /**
72 * Updates the foreground state of the service.
73 * @return Whether the service is foreground or not.
74 */
Daichi Hirono203be492017-04-03 12:20:42 +090075 private boolean updateForegroundState(
76 @Nullable int[] ids, @Nullable Notification[] notifications) {
Daichi Hironod3c6dd12017-01-26 14:40:19 +090077 final Set<Integer> openedNotification = new HashSet<>();
Daichi Hirono203be492017-04-03 12:20:42 +090078 final int size = ids != null ? ids.length : 0;
79 if (size != 0) {
80 Preconditions.checkArgument(ids != null);
81 Preconditions.checkArgument(notifications != null);
82 Preconditions.checkArgument(ids.length == notifications.length);
83 }
84
85 for (int i = 0; i < size; i++) {
86 if (i == 0) {
87 // Mark this service as foreground with the notification so that the process is
88 // not killed by the system while a MTP device is opened.
89 startForeground(ids[i], notifications[i]);
90 } else {
91 // Only one notification can be shown as a foreground notification. We need to
92 // show the rest as normal notification.
93 mNotificationManager.notify(ids[i], notifications[i]);
94 }
95 openedNotification.add(ids[i]);
96 }
Daichi Hironod3c6dd12017-01-26 14:40:19 +090097
98 final StatusBarNotification[] activeNotifications =
99 mNotificationManager.getActiveNotifications();
Daichi Hironod3c6dd12017-01-26 14:40:19 +0900100 for (final StatusBarNotification notification : activeNotifications) {
101 if (!openedNotification.contains(notification.getId())) {
102 mNotificationManager.cancel(notification.getId());
103 }
104 }
105
Daichi Hirono203be492017-04-03 12:20:42 +0900106 if (size == 0) {
Daichi Hironod3c6dd12017-01-26 14:40:19 +0900107 // There is no opened device.
Daichi Hironoa57d9ed2015-12-07 10:52:42 +0900108 stopForeground(true /* removeNotification */);
Daichi Hirono2efe4ca2015-07-27 16:47:46 +0900109 stopSelf();
Daichi Hironoa57d9ed2015-12-07 10:52:42 +0900110 return false;
Daichi Hirono2efe4ca2015-07-27 16:47:46 +0900111 }
Daichi Hironod3c6dd12017-01-26 14:40:19 +0900112
113 return true;
Daichi Hirono2efe4ca2015-07-27 16:47:46 +0900114 }
115
Daichi Hirono203be492017-04-03 12:20:42 +0900116 private static @NonNull Notification[] castToNotifications(@NonNull Parcelable[] src) {
117 Preconditions.checkNotNull(src);
118 final Notification[] notifications = new Notification[src.length];
119 for (int i = 0; i < src.length; i++) {
120 notifications[i] = (Notification) src[i];
121 }
122 return notifications;
Daichi Hirono2efe4ca2015-07-27 16:47:46 +0900123 }
124}