1 /*
2  * Copyright (c) 2019 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.ims;
18 
19 import android.content.Context;
20 import android.os.IInterface;
21 import android.os.RemoteCallbackList;
22 import android.telephony.SubscriptionManager;
23 import android.util.Log;
24 
25 
26 public abstract class ImsCallbackAdapterManager<T extends IInterface> {
27     private static final String TAG = "ImsCallbackAM";
28 
29     private final Context mContext;
30     private final Object mLock;
31     private final int mSlotId;
32     private final int mSubId;
33 
34     // List of all active callbacks to ImsService
35     private final RemoteCallbackList<T> mRemoteCallbacks = new RemoteCallbackList<>();
36 
ImsCallbackAdapterManager(Context context, Object lock, int slotId, int subId)37     public ImsCallbackAdapterManager(Context context, Object lock, int slotId, int subId) {
38         mContext = context;
39         mLock = lock;
40         mSlotId = slotId;
41         mSubId = subId;
42     }
43 
44     // Add a callback to the ImsFeature associated with this manager (independent of the
45     // current subscription).
addCallback(T localCallback)46     public final void addCallback(T localCallback) {
47         synchronized (mLock) {
48             // Skip registering to callback subscription map here, because we are registering
49             // for the slot, independent of subscription (deprecated behavior).
50             // Throws a IllegalStateException if this registration fails.
51             registerCallback(localCallback);
52             Log.i(TAG + " [" + mSlotId + "]", "Local callback added: " + localCallback);
53 
54             mRemoteCallbacks.register(localCallback);
55         }
56     }
57 
58     // Add a callback to be associated with a subscription.
addCallbackForSubscription(T localCallback, int subId)59     public void addCallbackForSubscription(T localCallback, int subId) {
60         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
61             Log.w(TAG + " [" + mSlotId + ", " + mSubId + "]", "add callback: invalid subId.");
62             return;
63         }
64         if (mSubId != subId) {
65             // In some cases, telephony has changed sub id and IMS is still catching up to the
66             // state change. Ensure that the device does not try to register a callback on an
67             // inactive subscription, because this can cause a condition where we remove the
68             // callback invisibly when the new subscription loads. Instead, simulate the existing
69             // IllegalStateException that happens when the ImsService is not ready/active for
70             // backwards compatibility.
71             Log.w(TAG + " [" + mSlotId + ", " + mSubId + "]", "add callback: inactive"
72                     + " subID detected: " + subId);
73             throw new IllegalStateException("ImsService is not available for the subscription "
74                     + "specified.");
75         }
76         synchronized (mLock) {
77             addCallback(localCallback);
78         }
79     }
80 
81     // Removes a callback associated with the ImsFeature.
removeCallback(T localCallback)82     public final void removeCallback(T localCallback) {
83         Log.i(TAG + " [" + mSlotId + "]", "Local callback removed: " + localCallback);
84         synchronized (mLock) {
85             if (mRemoteCallbacks.unregister(localCallback)) {
86                 // Will only occur if we have record of this callback in mRemoteCallbacks.
87                 unregisterCallback(localCallback);
88             }
89         }
90     }
91 
92     // The ImsService these callbacks are registered to has become unavailable or crashed, or
93     // the ImsResolver has switched to a new ImsService. In these cases, clean up all existing
94     // callbacks.
close()95     public final void close() {
96         synchronized (mLock) {
97             final int lastCallbackIndex = mRemoteCallbacks.getRegisteredCallbackCount() - 1;
98             for(int ii = lastCallbackIndex; ii >= 0; ii --) {
99                 T callbackItem = mRemoteCallbacks.getRegisteredCallbackItem(ii);
100                 unregisterCallback(callbackItem);
101                 mRemoteCallbacks.unregister(callbackItem);
102             }
103             Log.i(TAG + " [" + mSlotId + "]", "Closing connection and clearing callbacks");
104         }
105     }
106 
107     // A callback has been registered. Register that callback with the ImsFeature.
registerCallback(T localCallback)108     public abstract void registerCallback(T localCallback);
109 
110     // A callback has been removed, unregister that callback with the RcsFeature.
unregisterCallback(T localCallback)111     public abstract void unregisterCallback(T localCallback);
112 }
113