1/*
2* Copyright (C) 2015 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.internal.telephony;
18
19import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX;
20import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
21
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.os.Handler;
27import android.os.Registrant;
28import android.os.RegistrantList;
29import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.telephony.Rlog;
32import android.telephony.SubscriptionManager;
33import android.util.LocalLog;
34
35import com.android.internal.annotations.VisibleForTesting;
36import com.android.internal.telephony.ISub;
37import com.android.internal.telephony.IOnSubscriptionsChangedListener;
38import com.android.internal.telephony.ITelephonyRegistry;
39import com.android.internal.telephony.PhoneConstants;
40
41import java.io.FileDescriptor;
42import java.io.PrintWriter;
43import java.lang.IllegalArgumentException;
44
45/**
46 * Utility singleton to monitor subscription changes and help people act on them.
47 * Uses Registrant model to post messages to handlers.
48 *
49 */
50public class SubscriptionMonitor {
51
52    private final RegistrantList mSubscriptionsChangedRegistrants[];
53    private final RegistrantList mDefaultDataSubChangedRegistrants[];
54
55    private final SubscriptionController mSubscriptionController;
56    private final Context mContext;
57
58    private final int mPhoneSubId[];
59    private int mDefaultDataSubId;
60    private int mDefaultDataPhoneId;
61
62    private final Object mLock = new Object();
63
64    private final static boolean VDBG = true;
65    private final static String LOG_TAG = "SubscriptionMonitor";
66
67    private final static int MAX_LOGLINES = 100;
68    private final LocalLog mLocalLog = new LocalLog(MAX_LOGLINES);
69
70    public SubscriptionMonitor(ITelephonyRegistry tr, Context context,
71            SubscriptionController subscriptionController, int numPhones) {
72        try {
73            tr.addOnSubscriptionsChangedListener("SubscriptionMonitor",
74                    mSubscriptionsChangedListener);
75        } catch (RemoteException e) {
76        }
77
78        mSubscriptionController = subscriptionController;
79        mContext = context;
80
81        mSubscriptionsChangedRegistrants = new RegistrantList[numPhones];
82        mDefaultDataSubChangedRegistrants = new RegistrantList[numPhones];
83        mPhoneSubId = new int[numPhones];
84
85        mDefaultDataSubId = mSubscriptionController.getDefaultDataSubId();
86        mDefaultDataPhoneId = mSubscriptionController.getPhoneId(mDefaultDataSubId);
87
88        for (int phoneId = 0; phoneId < numPhones; phoneId++) {
89            mSubscriptionsChangedRegistrants[phoneId] = new RegistrantList();
90            mDefaultDataSubChangedRegistrants[phoneId] = new RegistrantList();
91            mPhoneSubId[phoneId] = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
92        }
93
94        mContext.registerReceiver(mDefaultDataSubscriptionChangedReceiver,
95                new IntentFilter(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED));
96    }
97
98    @VisibleForTesting
99    public SubscriptionMonitor() {
100        mSubscriptionsChangedRegistrants = null;
101        mDefaultDataSubChangedRegistrants = null;
102        mSubscriptionController = null;
103        mContext = null;
104        mPhoneSubId = null;
105    }
106
107    private final IOnSubscriptionsChangedListener mSubscriptionsChangedListener =
108            new IOnSubscriptionsChangedListener.Stub() {
109        @Override
110        public void onSubscriptionsChanged() {
111            synchronized (mLock) {
112                int newDefaultDataPhoneId = INVALID_PHONE_INDEX;
113                for (int phoneId = 0; phoneId < mPhoneSubId.length; phoneId++) {
114                    final int newSubId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
115                    final int oldSubId = mPhoneSubId[phoneId];
116                    if (oldSubId != newSubId) {
117                        log("Phone[" + phoneId + "] subId changed " + oldSubId + "->" +
118                                newSubId + ", " +
119                                mSubscriptionsChangedRegistrants[phoneId].size() + " registrants");
120                        mPhoneSubId[phoneId] = newSubId;
121                        mSubscriptionsChangedRegistrants[phoneId].notifyRegistrants();
122
123                        // if the default isn't set, just move along..
124                        if (mDefaultDataSubId == INVALID_SUBSCRIPTION_ID) continue;
125
126                        // check if this affects default data
127                        if (newSubId == mDefaultDataSubId || oldSubId == mDefaultDataSubId) {
128                            log("mDefaultDataSubId = " + mDefaultDataSubId + ", " +
129                                    mDefaultDataSubChangedRegistrants[phoneId].size() +
130                                    " registrants");
131                            mDefaultDataSubChangedRegistrants[phoneId].notifyRegistrants();
132                        }
133                    }
134                    if (newSubId == mDefaultDataSubId) {
135                        newDefaultDataPhoneId = phoneId;
136                    }
137                }
138                mDefaultDataPhoneId = newDefaultDataPhoneId;
139            }
140        }
141    };
142
143    private final BroadcastReceiver mDefaultDataSubscriptionChangedReceiver =
144            new BroadcastReceiver() {
145        @Override
146        public void onReceive(Context context, Intent intent) {
147            final int newDefaultDataSubId = mSubscriptionController.getDefaultDataSubId();
148            synchronized (mLock) {
149                if (mDefaultDataSubId != newDefaultDataSubId) {
150                    log("Default changed " + mDefaultDataSubId + "->" + newDefaultDataSubId);
151                    final int oldDefaultDataSubId = mDefaultDataSubId;
152                    final int oldDefaultDataPhoneId = mDefaultDataPhoneId;
153                    mDefaultDataSubId = newDefaultDataSubId;
154
155                    int newDefaultDataPhoneId =
156                            mSubscriptionController.getPhoneId(INVALID_SUBSCRIPTION_ID);
157                    if (newDefaultDataSubId != INVALID_SUBSCRIPTION_ID) {
158                        for (int phoneId = 0; phoneId < mPhoneSubId.length; phoneId++) {
159                            if (mPhoneSubId[phoneId] == newDefaultDataSubId) {
160                                newDefaultDataPhoneId = phoneId;
161                                if (VDBG) log("newDefaultDataPhoneId=" + newDefaultDataPhoneId);
162                                break;
163                            }
164                        }
165                    }
166                    if (newDefaultDataPhoneId != oldDefaultDataPhoneId) {
167                        log("Default phoneId changed " + oldDefaultDataPhoneId + "->" +
168                                newDefaultDataPhoneId + ", " +
169                                (invalidPhoneId(oldDefaultDataPhoneId) ?
170                                 0 :
171                                 mDefaultDataSubChangedRegistrants[oldDefaultDataPhoneId].size()) +
172                                "," + (invalidPhoneId(newDefaultDataPhoneId) ?
173                                  0 :
174                                  mDefaultDataSubChangedRegistrants[newDefaultDataPhoneId].size()) +
175                                " registrants");
176                        mDefaultDataPhoneId = newDefaultDataPhoneId;
177                        if (!invalidPhoneId(oldDefaultDataPhoneId)) {
178                            mDefaultDataSubChangedRegistrants[oldDefaultDataPhoneId].
179                                    notifyRegistrants();
180                        }
181                        if (!invalidPhoneId(newDefaultDataPhoneId)) {
182                            mDefaultDataSubChangedRegistrants[newDefaultDataPhoneId].
183                                    notifyRegistrants();
184                        }
185                    }
186                }
187            }
188        }
189    };
190
191    public void registerForSubscriptionChanged(int phoneId, Handler h, int what, Object o) {
192        if (invalidPhoneId(phoneId)) {
193            throw new IllegalArgumentException("Invalid PhoneId");
194        }
195        Registrant r = new Registrant(h, what, o);
196        mSubscriptionsChangedRegistrants[phoneId].add(r);
197        r.notifyRegistrant();
198    }
199
200    public void unregisterForSubscriptionChanged(int phoneId, Handler h) {
201        if (invalidPhoneId(phoneId)) {
202            throw new IllegalArgumentException("Invalid PhoneId");
203        }
204        mSubscriptionsChangedRegistrants[phoneId].remove(h);
205    }
206
207    public void registerForDefaultDataSubscriptionChanged(int phoneId, Handler h, int what,
208            Object o) {
209        if (invalidPhoneId(phoneId)) {
210            throw new IllegalArgumentException("Invalid PhoneId");
211        }
212        Registrant r = new Registrant(h, what, o);
213        mDefaultDataSubChangedRegistrants[phoneId].add(r);
214        r.notifyRegistrant();
215    }
216
217    public void unregisterForDefaultDataSubscriptionChanged(int phoneId, Handler h) {
218        if (invalidPhoneId(phoneId)) {
219            throw new IllegalArgumentException("Invalid PhoneId");
220        }
221        mDefaultDataSubChangedRegistrants[phoneId].remove(h);
222    }
223
224    private boolean invalidPhoneId(int phoneId) {
225        if (phoneId >= 0 && phoneId < mPhoneSubId.length) return false;
226        return true;
227    }
228
229    private void log(String s) {
230        Rlog.d(LOG_TAG, s);
231        mLocalLog.log(s);
232    }
233
234    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
235        synchronized (mLock) {
236            mLocalLog.dump(fd, printWriter, args);
237        }
238    }
239}
240