blob: 0056e1b41b93c4f521712adfa853dd35f461355a [file] [log] [blame]
Matthew Ng13dbf872017-10-27 11:02:14 -07001/*
2 * Copyright (C) 2017 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.systemui;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
Winson Chung38d31c22017-11-08 14:32:32 -080023import android.graphics.Bitmap;
24import android.graphics.Rect;
25import android.os.Binder;
Matthew Ng13dbf872017-10-27 11:02:14 -070026import android.os.Handler;
27import android.os.IBinder;
28import android.os.RemoteException;
29import android.os.UserHandle;
30import android.util.Log;
Winson Chung38d31c22017-11-08 14:32:32 -080031import android.view.SurfaceControl;
32
33import com.android.systemui.shared.recents.IOverviewProxy;
34import com.android.systemui.shared.recents.ISystemUiProxy;
Matthew Ng7d05e772017-11-09 14:41:07 -080035import com.android.systemui.OverviewProxyService.OverviewProxyListener;
36import com.android.systemui.statusbar.policy.CallbackController;
Matthew Ng13dbf872017-10-27 11:02:14 -070037import com.android.systemui.statusbar.policy.DeviceProvisionedController;
38import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
Matthew Ng1e43ebd2017-11-14 14:47:05 -080039import java.io.FileDescriptor;
40import java.io.PrintWriter;
Matthew Ng7d05e772017-11-09 14:41:07 -080041import java.util.ArrayList;
42import java.util.List;
Matthew Ng13dbf872017-10-27 11:02:14 -070043
44/**
45 * Class to send information from overview to launcher with a binder.
46 */
Matthew Ng1e43ebd2017-11-14 14:47:05 -080047public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
Matthew Ng13dbf872017-10-27 11:02:14 -070048
49 private static final String TAG = "OverviewProxyService";
50 private static final long BACKOFF_MILLIS = 5000;
51
52 private final Context mContext;
53 private final Handler mHandler;
54 private final Runnable mConnectionRunnable = this::startConnectionToCurrentUser;
55 private final DeviceProvisionedController mDeviceProvisionedController
56 = Dependency.get(DeviceProvisionedController.class);
Matthew Ng7d05e772017-11-09 14:41:07 -080057 private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
Matthew Ng13dbf872017-10-27 11:02:14 -070058
59 private IOverviewProxy mOverviewProxy;
60 private int mConnectionBackoffAttempts;
61
Winson Chung38d31c22017-11-08 14:32:32 -080062 private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
63 public Bitmap screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
64 boolean useIdentityTransform, int rotation) {
65 long token = Binder.clearCallingIdentity();
66 try {
67 return SurfaceControl.screenshot(sourceCrop, width, height, minLayer, maxLayer,
68 useIdentityTransform, rotation);
69 } finally {
70 Binder.restoreCallingIdentity(token);
71 }
72 }
73 };
74
Matthew Ng13dbf872017-10-27 11:02:14 -070075 private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
76 @Override
77 public void onServiceConnected(ComponentName name, IBinder service) {
78 if (service != null) {
79 mConnectionBackoffAttempts = 0;
80 mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
81 // Listen for launcher's death
82 try {
83 service.linkToDeath(mOverviewServiceDeathRcpt, 0);
84 } catch (RemoteException e) {
85 Log.e(TAG, "Lost connection to launcher service", e);
86 }
Winson Chung38d31c22017-11-08 14:32:32 -080087 try {
88 mOverviewProxy.onBind(mSysUiProxy);
89 } catch (RemoteException e) {
90 Log.e(TAG, "Failed to call onBind()", e);
91 }
Matthew Ng7d05e772017-11-09 14:41:07 -080092 notifyConnectionChanged();
Matthew Ng13dbf872017-10-27 11:02:14 -070093 }
94 }
95
96 @Override
97 public void onServiceDisconnected(ComponentName name) {
98 // Do nothing
99 }
100 };
101
102 private final DeviceProvisionedListener mDeviceProvisionedCallback =
103 new DeviceProvisionedListener() {
104 @Override
Matthew Ngdfab86c2017-11-07 15:46:51 -0800105 public void onUserSetupChanged() {
Matthew Ng13dbf872017-10-27 11:02:14 -0700106 if (mDeviceProvisionedController.isCurrentUserSetup()) {
107 startConnectionToCurrentUser();
108 }
109 }
110
111 @Override
112 public void onUserSwitched() {
Matthew Ng13dbf872017-10-27 11:02:14 -0700113 mConnectionBackoffAttempts = 0;
114 startConnectionToCurrentUser();
115 }
116 };
117
118 // This is the death handler for the binder from the launcher service
119 private final IBinder.DeathRecipient mOverviewServiceDeathRcpt = new IBinder.DeathRecipient() {
120 @Override
121 public void binderDied() {
Matthew Ngeb6893b2017-11-09 17:15:33 -0800122 startConnectionToCurrentUser();
Matthew Ng13dbf872017-10-27 11:02:14 -0700123 }
124 };
125
126 public OverviewProxyService(Context context) {
127 mContext = context;
128 mHandler = new Handler();
129 mConnectionBackoffAttempts = 0;
130 mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
131 }
132
133 public void startConnectionToCurrentUser() {
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800134 disconnectFromLauncherService();
135
Matthew Ng13dbf872017-10-27 11:02:14 -0700136 // If user has not setup yet or already connected, do not try to connect
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800137 if (!mDeviceProvisionedController.isCurrentUserSetup()) {
Matthew Ng13dbf872017-10-27 11:02:14 -0700138 return;
139 }
140 mHandler.removeCallbacks(mConnectionRunnable);
141 Intent launcherServiceIntent = new Intent();
142 launcherServiceIntent.setComponent(ComponentName.unflattenFromString(
143 mContext.getString(R.string.config_overviewServiceComponent)));
144 boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
145 mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
146 UserHandle.getUserHandleForUid(mDeviceProvisionedController.getCurrentUser()));
147 if (!bound) {
148 // Retry after exponential backoff timeout
149 final long timeoutMs = (long) Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts);
150 mHandler.postDelayed(mConnectionRunnable, timeoutMs);
151 mConnectionBackoffAttempts++;
152 }
153 }
154
Matthew Ng7d05e772017-11-09 14:41:07 -0800155 @Override
156 public void addCallback(OverviewProxyListener listener) {
157 mConnectionCallbacks.add(listener);
158 listener.onConnectionChanged(mOverviewProxy != null);
159 }
160
161 @Override
162 public void removeCallback(OverviewProxyListener listener) {
163 mConnectionCallbacks.remove(listener);
164 }
165
Matthew Ng13dbf872017-10-27 11:02:14 -0700166 public IOverviewProxy getProxy() {
167 return mOverviewProxy;
168 }
169
170 private void disconnectFromLauncherService() {
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800171 if (mOverviewProxy != null) {
Matthew Ngeb6893b2017-11-09 17:15:33 -0800172 mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800173 mContext.unbindService(mOverviewServiceConnection);
174 mOverviewProxy = null;
Matthew Ng7d05e772017-11-09 14:41:07 -0800175 notifyConnectionChanged();
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800176 }
Matthew Ng13dbf872017-10-27 11:02:14 -0700177 }
Matthew Ng7d05e772017-11-09 14:41:07 -0800178
179 private void notifyConnectionChanged() {
180 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
181 mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
182 }
183 }
184
Matthew Ng1e43ebd2017-11-14 14:47:05 -0800185 @Override
186 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
187 pw.println(TAG + " state:");
188 pw.print(" mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
189 pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController
190 .isCurrentUserSetup());
191 pw.print(" isConnected="); pw.println(mOverviewProxy != null);
192 }
193
Matthew Ng7d05e772017-11-09 14:41:07 -0800194 public interface OverviewProxyListener {
195 void onConnectionChanged(boolean isConnected);
196 }
Matthew Ng13dbf872017-10-27 11:02:14 -0700197}