1 /* 2 * Copyright (C) 2016 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 android.printservice.recommendation; 18 19 import android.annotation.Nullable; 20 import android.annotation.SystemApi; 21 import android.app.Service; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.os.Handler; 25 import android.os.IBinder; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.os.RemoteException; 29 import android.util.Log; 30 31 import java.util.List; 32 33 /** 34 * Base class for the print service recommendation services. 35 * 36 * @hide 37 */ 38 @SystemApi 39 public abstract class RecommendationService extends Service { 40 private static final String LOG_TAG = "PrintServiceRecS"; 41 42 /** Used to push onConnect and onDisconnect on the main thread */ 43 private Handler mHandler; 44 45 /** 46 * The {@link Intent} action that must be declared as handled by a service in its manifest for 47 * the system to recognize it as a print service recommendation service. 48 */ 49 public static final String SERVICE_INTERFACE = 50 "android.printservice.recommendation.RecommendationService"; 51 52 /** Registered callbacks, only modified on main thread */ 53 private IRecommendationServiceCallbacks mCallbacks; 54 55 @Override attachBaseContext(Context base)56 protected void attachBaseContext(Context base) { 57 super.attachBaseContext(base); 58 59 mHandler = new MyHandler(); 60 } 61 62 /** 63 * Update the print service recommendations. 64 * 65 * @param recommendations The new set of recommendations 66 */ updateRecommendations(@ullable List<RecommendationInfo> recommendations)67 public final void updateRecommendations(@Nullable List<RecommendationInfo> recommendations) { 68 mHandler.obtainMessage(MyHandler.MSG_UPDATE, recommendations).sendToTarget(); 69 } 70 71 @Override onBind(Intent intent)72 public final IBinder onBind(Intent intent) { 73 return new IRecommendationService.Stub() { 74 @Override 75 public void registerCallbacks(IRecommendationServiceCallbacks callbacks) { 76 // The callbacks come in order of the caller on oneway calls. Hence while the caller 77 // cannot know at what time the connection is made, he can know the ordering of 78 // connection and disconnection. 79 // 80 // Similar he cannot know when the disconnection is processed, hence he has to 81 // handle callbacks after calling disconnect. 82 if (callbacks != null) { 83 mHandler.obtainMessage(MyHandler.MSG_CONNECT, callbacks).sendToTarget(); 84 } else { 85 mHandler.obtainMessage(MyHandler.MSG_DISCONNECT).sendToTarget(); 86 } 87 } 88 }; 89 } 90 91 /** 92 * Called when the client connects to the recommendation service. 93 */ 94 public abstract void onConnected(); 95 96 /** 97 * Called when the client disconnects from the recommendation service. 98 */ 99 public abstract void onDisconnected(); 100 101 private class MyHandler extends Handler { 102 static final int MSG_CONNECT = 1; 103 static final int MSG_DISCONNECT = 2; 104 static final int MSG_UPDATE = 3; 105 106 MyHandler() { 107 super(Looper.getMainLooper()); 108 } 109 110 @Override 111 public void handleMessage(Message msg) { 112 switch (msg.what) { 113 case MSG_CONNECT: 114 mCallbacks = (IRecommendationServiceCallbacks) msg.obj; 115 onConnected(); 116 break; 117 case MSG_DISCONNECT: 118 onDisconnected(); 119 mCallbacks = null; 120 break; 121 case MSG_UPDATE: 122 if (mCallbacks != null) { 123 // Note that there might be a connection change in progress. In this case 124 // the message is handled as before the change. This is acceptable as the 125 // caller of the connection change has not guarantee when the connection 126 // change binder transaction is actually processed. 127 try { 128 mCallbacks.onRecommendationsUpdated((List<RecommendationInfo>) msg.obj); 129 } catch (RemoteException | NullPointerException e) { 130 Log.e(LOG_TAG, "Could not update recommended services", e); 131 } 132 } 133 break; 134 } 135 } 136 } 137 } 138