1 /*
2  * Copyright (C) 2014 The Android Open Sour *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package com.android.cts.launchertests.support;
17 
18 import android.app.Service;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.pm.LauncherApps;
22 import android.content.pm.PackageManager.NameNotFoundException;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.HandlerThread;
26 import android.os.IBinder;
27 import android.os.Message;
28 import android.os.Messenger;
29 import android.os.RemoteException;
30 import android.os.UserHandle;
31 import android.util.Log;
32 import android.util.Pair;
33 
34 import java.util.ArrayList;
35 import java.util.concurrent.BlockingQueue;
36 import java.util.concurrent.LinkedBlockingQueue;
37 import java.util.concurrent.TimeUnit;
38 import java.util.List;
39 
40 /**
41  * Service that registers for LauncherApps callbacks.
42  *
43  * Registering in a service and different process so that
44  * device side code can launch it before running client
45  * side test code.
46  */
47 public class LauncherCallbackTestsService extends Service {
48 
49     public static final String USER_EXTRA = "user_extra";
50     public static final String PACKAGE_EXTRA = "package_extra";
51 
52     public static final int MSG_RESULT = 0;
53     public static final int MSG_CHECK_PACKAGE_ADDED = 1;
54     public static final int MSG_CHECK_PACKAGE_REMOVED = 2;
55     public static final int MSG_CHECK_PACKAGE_CHANGED = 3;
56     public static final int MSG_CHECK_NO_PACKAGE_ADDED = 4;
57 
58     public static final int RESULT_PASS = 1;
59     public static final int RESULT_FAIL = 2;
60 
61     private static final String TAG = "LauncherCallbackTests";
62 
63     private static BlockingQueue<Pair<String, UserHandle>> mPackagesAdded
64             = new LinkedBlockingQueue();
65     private static BlockingQueue<Pair<String, UserHandle>> mPackagesRemoved
66             = new LinkedBlockingQueue();
67     private static BlockingQueue<Pair<String, UserHandle>> mPackagesChanged
68             = new LinkedBlockingQueue();
69 
70     private TestCallback mCallback;
71     private Object mCallbackLock = new Object();
72     private final Messenger mMessenger = new Messenger(new CheckHandler());
73     private final HandlerThread mCallbackThread = new HandlerThread("callback");
74 
LauncherCallbackTestsService()75     public LauncherCallbackTestsService() {
76         mCallbackThread.start();
77     }
78 
79     class CheckHandler extends Handler {
80         @Override
handleMessage(Message msg)81         public void handleMessage(Message msg) {
82             Bundle params = null;
83             if (msg.obj instanceof Bundle) {
84                 params = (Bundle) (msg.obj);
85             }
86             try {
87                 switch (msg.what) {
88                     case MSG_CHECK_PACKAGE_ADDED: {
89                         Log.i(TAG, "MSG_CHECK_PACKAGE_ADDED");
90                         boolean exists = eventExists(params, mPackagesAdded);
91                         teardown();
92                         msg.replyTo.send(Message.obtain(null, MSG_RESULT,
93                                         exists ? RESULT_PASS : RESULT_FAIL, 0));
94                         break;
95                     }
96                     case MSG_CHECK_PACKAGE_REMOVED: {
97                         Log.i(TAG, "MSG_CHECK_PACKAGE_REMOVED");
98                         boolean exists = eventExists(params, mPackagesRemoved);
99                         teardown();
100                         msg.replyTo.send(Message.obtain(null, MSG_RESULT,
101                                         exists ? RESULT_PASS : RESULT_FAIL, 0));
102                         break;
103                     }
104                     case MSG_CHECK_PACKAGE_CHANGED: {
105                         Log.i(TAG, "MSG_CHECK_PACKAGE_CHANGED");
106                         boolean exists = eventExists(params, mPackagesChanged);
107                         teardown();
108                         msg.replyTo.send(Message.obtain(null, MSG_RESULT,
109                                         exists ? RESULT_PASS : RESULT_FAIL, 0));
110                         break;
111                     }
112                     case MSG_CHECK_NO_PACKAGE_ADDED: {
113                         Log.i(TAG, "MSG_CHECK_NO_PACKAGE_ADDED");
114                         boolean exists = eventExists(params, mPackagesAdded);
115                         teardown();
116                         msg.replyTo.send(Message.obtain(null, MSG_RESULT,
117                                         exists ? RESULT_FAIL : RESULT_PASS, 0));
118                         break;
119                     }
120                     default:
121                         super.handleMessage(msg);
122                 }
123             } catch (RemoteException e) {
124                 Log.e(TAG, "Failed to report test status");
125             }
126         }
127     }
128 
129     @Override
onStartCommand(Intent intent, int flags, int startId)130     public int onStartCommand(Intent intent, int flags, int startId) {
131         if (intent == null) {
132             return START_NOT_STICKY;
133         }
134         if ("com.android.cts.launchertests.support.REGISTER_CALLBACK".equals(intent.getAction())) {
135             setup();
136         }
137         return START_STICKY;
138     }
139 
setup()140     private void setup() {
141         LauncherApps launcherApps = (LauncherApps) getSystemService(
142                 Context.LAUNCHER_APPS_SERVICE);
143         synchronized (mCallbackLock) {
144             if (mCallback != null) {
145                 launcherApps.unregisterCallback(mCallback);
146             }
147             mPackagesAdded.clear();
148             mPackagesRemoved.clear();
149             mPackagesChanged.clear();
150             mCallback = new TestCallback();
151             launcherApps.registerCallback(mCallback, new Handler(mCallbackThread.getLooper()));
152             Log.i(TAG, "started listening for events");
153         }
154     }
155 
teardown()156     private void teardown() {
157         LauncherApps launcherApps = (LauncherApps) getSystemService(
158                 Context.LAUNCHER_APPS_SERVICE);
159         synchronized (mCallbackLock) {
160             if (mCallback != null) {
161                 launcherApps.unregisterCallback(mCallback);
162                 Log.i(TAG, "stopped listening for events");
163                 mCallback = null;
164             }
165             mPackagesAdded.clear();
166             mPackagesRemoved.clear();
167             mPackagesChanged.clear();
168         }
169     }
170 
eventExists(Bundle params, BlockingQueue<Pair<String, UserHandle>> events)171     private boolean eventExists(Bundle params, BlockingQueue<Pair<String, UserHandle>> events) {
172         UserHandle user = params.getParcelable(USER_EXTRA);
173         String packageName = params.getString(PACKAGE_EXTRA);
174         Log.i(TAG, "checking for " + packageName + " " + user);
175         try {
176             Pair<String, UserHandle> event = events.poll(60, TimeUnit.SECONDS);
177             while (event != null) {
178                 if (event.first.equals(packageName) && event.second.equals(user)) {
179                     Log.i(TAG, "Event exists " + packageName + " for user " + user);
180                     return true;
181                 }
182                 event = events.poll(20, TimeUnit.SECONDS);
183             }
184         } catch (InterruptedException e) {
185             Log.e(TAG, "Failed checking for event", e);
186         }
187         return false;
188     }
189 
190     @Override
onBind(Intent intent)191     public IBinder onBind(Intent intent) {
192         return mMessenger.getBinder();
193     }
194 
195     private class TestCallback extends LauncherApps.Callback {
onPackageRemoved(String packageName, UserHandle user)196         public void onPackageRemoved(String packageName, UserHandle user) {
197             Log.i(TAG, "package removed event " + packageName + " " + user);
198             try {
199                 mPackagesRemoved.put(new Pair(packageName, user));
200             } catch (InterruptedException e) {
201                 Log.e(TAG, "Failed saving event", e);
202             }
203         }
204 
onPackageAdded(String packageName, UserHandle user)205         public void onPackageAdded(String packageName, UserHandle user) {
206             Log.i(TAG, "package added event " + packageName + " " + user);
207             try {
208                 mPackagesAdded.put(new Pair(packageName, user));
209             } catch (InterruptedException e) {
210                 Log.e(TAG, "Failed saving event", e);
211             }
212         }
213 
onPackageChanged(String packageName, UserHandle user)214         public void onPackageChanged(String packageName, UserHandle user) {
215             Log.i(TAG, "package changed event " + packageName + " " + user);
216             try {
217                 mPackagesChanged.put(new Pair(packageName, user));
218             } catch (InterruptedException e) {
219                 Log.e(TAG, "Failed saving event", e);
220             }
221         }
222 
onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)223         public void onPackagesAvailable(String[] packageNames, UserHandle user,
224                 boolean replacing) {
225         }
226 
onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)227         public void onPackagesUnavailable(String[] packageNames, UserHandle user,
228                 boolean replacing) {
229         }
230     }
231 }
232