• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.companion;
18 
19 import static android.content.Context.BIND_ALMOST_PERCEPTIBLE;
20 import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE;
21 import static android.os.Process.THREAD_PRIORITY_DEFAULT;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.SuppressLint;
26 import android.annotation.UserIdInt;
27 import android.companion.AssociationInfo;
28 import android.companion.CompanionDeviceService;
29 import android.companion.ICompanionDeviceService;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.os.Handler;
34 import android.os.IBinder;
35 import android.util.Log;
36 
37 import com.android.internal.infra.ServiceConnector;
38 import com.android.server.ServiceThread;
39 
40 /**
41  * Manages a connection (binding) to an instance of {@link CompanionDeviceService} running in the
42  * application process.
43  */
44 @SuppressLint("LongLogTag")
45 class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDeviceService> {
46     private static final String TAG = "CompanionDevice_ServiceConnector";
47     private static final boolean DEBUG = false;
48 
49     /** Listener for changes to the state of the {@link CompanionDeviceServiceConnector}  */
50     interface Listener {
onBindingDied(@serIdInt int userId, @NonNull String packageName)51         void onBindingDied(@UserIdInt int userId, @NonNull String packageName);
52     }
53 
54     private final @UserIdInt int mUserId;
55     private final @NonNull ComponentName mComponentName;
56     // IMPORTANT: this can (and will!) be null (at the moment, CompanionApplicationController only
57     // installs a listener to the primary ServiceConnector), hence we should always null-check the
58     // reference before calling on it.
59     private @Nullable Listener mListener;
60 
61     /**
62      * Create a CompanionDeviceServiceConnector instance.
63      *
64      * For self-managed apps, the binding flag will be BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE
65      * (oom_score_adj = VISIBLE_APP_ADJ = 100).
66      *
67      * For non self-managed apps, the binding flag will be BIND_ALMOST_PERCEPTIBLE
68      * (oom_score_adj = PERCEPTIBLE_MEDIUM_APP = 225). The target service will be treated
69      * as important as a perceptible app (IMPORTANCE_VISIBLE = 200), and will be unbound when
70      * the app is removed from task manager.
71      *
72      * One time permission's importance level to keep session alive is
73      * IMPORTANCE_FOREGROUND_SERVICE = 125. In order to kill the one time permission session, the
74      * service importance level should be higher than 125.
75      */
newInstance(@onNull Context context, @UserIdInt int userId, @NonNull ComponentName componentName, boolean isSelfManaged)76     static CompanionDeviceServiceConnector newInstance(@NonNull Context context,
77             @UserIdInt int userId, @NonNull ComponentName componentName, boolean isSelfManaged) {
78         final int bindingFlags = isSelfManaged ? BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE
79                 : BIND_ALMOST_PERCEPTIBLE;
80         return new CompanionDeviceServiceConnector(context, userId, componentName, bindingFlags);
81     }
82 
CompanionDeviceServiceConnector(@onNull Context context, @UserIdInt int userId, @NonNull ComponentName componentName, int bindingFlags)83     private CompanionDeviceServiceConnector(@NonNull Context context, @UserIdInt int userId,
84             @NonNull ComponentName componentName, int bindingFlags) {
85         super(context, buildIntent(componentName), bindingFlags, userId, null);
86         mUserId = userId;
87         mComponentName = componentName;
88     }
89 
setListener(@ullable Listener listener)90     void setListener(@Nullable Listener listener) {
91         mListener = listener;
92     }
93 
postOnDeviceAppeared(@onNull AssociationInfo associationInfo)94     void postOnDeviceAppeared(@NonNull AssociationInfo associationInfo) {
95         post(companionService -> companionService.onDeviceAppeared(associationInfo));
96     }
97 
postOnDeviceDisappeared(@onNull AssociationInfo associationInfo)98     void postOnDeviceDisappeared(@NonNull AssociationInfo associationInfo) {
99         post(companionService -> companionService.onDeviceDisappeared(associationInfo));
100     }
101 
102     /**
103      * Post "unbind" job, which will run *after* all previously posted jobs complete.
104      *
105      * IMPORTANT: use this method instead of invoking {@link ServiceConnector#unbind()} directly,
106      * because the latter may cause previously posted callback, such as
107      * {@link ICompanionDeviceService#onDeviceDisappeared(AssociationInfo)} to be dropped.
108      */
postUnbind()109     void postUnbind() {
110         post(it -> unbind());
111     }
112 
113     @Override
onServiceConnectionStatusChanged( @onNull ICompanionDeviceService service, boolean isConnected)114     protected void onServiceConnectionStatusChanged(
115             @NonNull ICompanionDeviceService service, boolean isConnected) {
116         if (DEBUG) {
117             Log.d(TAG, "onServiceConnection_StatusChanged() " + mComponentName.toShortString()
118                     + " connected=" + isConnected);
119         }
120     }
121 
122     // This method is only called when app is force-closed via settings,
123     // but does not handle crashes. Binder death should be handled in #binderDied()
124     @Override
onBindingDied(@onNull ComponentName name)125     public void onBindingDied(@NonNull ComponentName name) {
126         // IMPORTANT: call super! this will also invoke binderDied()
127         super.onBindingDied(name);
128 
129         if (DEBUG) Log.d(TAG, "onBindingDied() " + mComponentName.toShortString());
130     }
131 
132     @Override
binderDied()133     public void binderDied() {
134         super.binderDied();
135 
136         if (DEBUG) Log.d(TAG, "binderDied() " + mComponentName.toShortString());
137 
138         // Handle primary process being killed
139         if (mListener != null) {
140             mListener.onBindingDied(mUserId, mComponentName.getPackageName());
141         }
142     }
143 
144     @Override
binderAsInterface(@onNull IBinder service)145     protected ICompanionDeviceService binderAsInterface(@NonNull IBinder service) {
146         return ICompanionDeviceService.Stub.asInterface(service);
147     }
148 
149     /**
150      * Overrides {@link ServiceConnector.Impl#getJobHandler()} to provide an alternative Thread
151      * ("in form of" a {@link Handler}) to process jobs on.
152      * <p>
153      * (By default, {@link ServiceConnector.Impl} process jobs on the
154      * {@link android.os.Looper#getMainLooper() MainThread} which is a shared singleton thread
155      * within system_server and thus tends to get heavily congested)
156      */
157     @Override
getJobHandler()158     protected @NonNull Handler getJobHandler() {
159         return getServiceThread().getThreadHandler();
160     }
161 
162     @Override
getAutoDisconnectTimeoutMs()163     protected long getAutoDisconnectTimeoutMs() {
164         // Do NOT auto-disconnect.
165         return -1;
166     }
167 
buildIntent(@onNull ComponentName componentName)168     private static @NonNull Intent buildIntent(@NonNull ComponentName componentName) {
169         return new Intent(CompanionDeviceService.SERVICE_INTERFACE)
170                 .setComponent(componentName);
171     }
172 
getServiceThread()173     private static @NonNull ServiceThread getServiceThread() {
174         if (sServiceThread == null) {
175             synchronized (CompanionDeviceManagerService.class) {
176                 if (sServiceThread == null) {
177                     sServiceThread = new ServiceThread("companion-device-service-connector",
178                             THREAD_PRIORITY_DEFAULT, /* allowIo */ false);
179                     sServiceThread.start();
180                 }
181             }
182         }
183         return sServiceThread;
184     }
185 
186     /**
187      * A worker thread for the {@link ServiceConnector} to process jobs on.
188      *
189      * <p>
190      *  Do NOT reference directly, use {@link #getServiceThread()} method instead.
191      */
192     private static volatile @Nullable ServiceThread sServiceThread;
193 }
194