1 /* 2 * Copyright (C) 2018 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.internal.telephony.ims; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.os.IBinder; 24 import android.os.RemoteException; 25 import android.telephony.ims.aidl.IImsServiceController; 26 import android.telephony.ims.stub.ImsFeatureConfiguration; 27 import android.util.Log; 28 29 import java.util.HashMap; 30 import java.util.Map; 31 import java.util.Set; 32 33 /** 34 * Manages the querying of multiple ImsServices asynchronously in order to retrieve the ImsFeatures 35 * they support. 36 */ 37 38 public class ImsServiceFeatureQueryManager { 39 40 private final class ImsServiceFeatureQuery implements ServiceConnection { 41 42 private static final String LOG_TAG = "ImsServiceFeatureQuery"; 43 44 private final ComponentName mName; 45 private final String mIntentFilter; 46 47 ImsServiceFeatureQuery(ComponentName name, String intentFilter) { 48 mName = name; 49 mIntentFilter = intentFilter; 50 } 51 52 /** 53 * Starts the bind to the ImsService specified ComponentName. 54 * @return true if binding started, false if it failed and will not recover. 55 */ 56 public boolean start() { 57 Log.d(LOG_TAG, "start: intent filter=" + mIntentFilter + ", name=" + mName); 58 Intent imsServiceIntent = new Intent(mIntentFilter).setComponent(mName); 59 int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE 60 | Context.BIND_IMPORTANT; 61 boolean bindStarted = mContext.bindService(imsServiceIntent, this, serviceFlags); 62 if (!bindStarted) { 63 // Docs say to unbind if this fails. 64 cleanup(); 65 } 66 return bindStarted; 67 } 68 69 @Override 70 public void onServiceConnected(ComponentName name, IBinder service) { 71 Log.i(LOG_TAG, "onServiceConnected for component: " + name); 72 if (service != null) { 73 queryImsFeatures(IImsServiceController.Stub.asInterface(service)); 74 } else { 75 Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null, cleaning up."); 76 cleanup(); 77 } 78 } 79 80 @Override 81 public void onServiceDisconnected(ComponentName name) { 82 Log.w(LOG_TAG, "onServiceDisconnected for component: " + name); 83 } 84 85 private void queryImsFeatures(IImsServiceController controller) { 86 ImsFeatureConfiguration config; 87 try { 88 config = controller.querySupportedImsFeatures(); 89 } catch (RemoteException e) { 90 Log.w(LOG_TAG, "queryImsFeatures - error: " + e); 91 cleanup(); 92 mListener.onError(mName); 93 return; 94 } 95 Set<ImsFeatureConfiguration.FeatureSlotPair> servicePairs = config.getServiceFeatures(); 96 // Complete, remove from active queries and notify. 97 cleanup(); 98 mListener.onComplete(mName, servicePairs); 99 } 100 101 private void cleanup() { 102 mContext.unbindService(this); 103 synchronized (mLock) { 104 mActiveQueries.remove(mName); 105 } 106 } 107 } 108 109 public interface Listener { 110 /** 111 * Called when a query has completed. 112 * @param name The Package Name of the query 113 * @param features A Set of slotid->feature pairs that the ImsService supports. 114 */ 115 void onComplete(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features); 116 117 /** 118 * Called when a query has failed and should be retried. 119 */ 120 void onError(ComponentName name); 121 } 122 123 // Maps an active ImsService query (by Package Name String) its query. 124 private final Map<ComponentName, ImsServiceFeatureQuery> mActiveQueries = new HashMap<>(); 125 private final Context mContext; 126 private final Listener mListener; 127 private final Object mLock = new Object(); 128 129 public ImsServiceFeatureQueryManager(Context context, Listener listener) { 130 mContext = context; 131 mListener = listener; 132 } 133 134 /** 135 * Starts an ImsService feature query for the ComponentName and Intent specified. 136 * @param name The ComponentName of the ImsService being queried. 137 * @param intentFilter The Intent filter that the ImsService specified. 138 * @return true if the query started, false if it was unable to start. 139 */ 140 public boolean startQuery(ComponentName name, String intentFilter) { 141 synchronized (mLock) { 142 if (mActiveQueries.containsKey(name)) { 143 // We already have an active query, wait for it to return. 144 return true; 145 } 146 ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, intentFilter); 147 mActiveQueries.put(name, query); 148 return query.start(); 149 } 150 } 151 152 /** 153 * @return true if there are any active queries, false if the manager is idle. 154 */ 155 public boolean isQueryInProgress() { 156 synchronized (mLock) { 157 return !mActiveQueries.isEmpty(); 158 } 159 } 160 } 161