15f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger/*
25f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * Copyright (C) 2018 The Android Open Source Project
35f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger *
45f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * Licensed under the Apache License, Version 2.0 (the "License");
55f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * you may not use this file except in compliance with the License.
65f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * You may obtain a copy of the License at
75f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger *
85f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger *      http://www.apache.org/licenses/LICENSE-2.0
95f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger *
105f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * Unless required by applicable law or agreed to in writing, software
115f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * distributed under the License is distributed on an "AS IS" BASIS,
125f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * See the License for the specific language governing permissions and
145f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * limitations under the License.
155f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger */
165f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
175f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerpackage com.android.internal.telephony.ims;
185f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
195f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport android.content.ComponentName;
205f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport android.content.Context;
215f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport android.content.Intent;
225f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport android.content.ServiceConnection;
235f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport android.os.IBinder;
245f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport android.os.RemoteException;
255f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport android.telephony.ims.aidl.IImsServiceController;
265f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport android.telephony.ims.stub.ImsFeatureConfiguration;
275f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport android.util.Log;
285f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
295f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport java.util.HashMap;
305f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport java.util.Map;
315f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerimport java.util.Set;
325f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
335f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger/**
345f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * Manages the querying of multiple ImsServices asynchronously in order to retrieve the ImsFeatures
355f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger * they support.
365f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger */
375f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
385f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebingerpublic class ImsServiceFeatureQueryManager {
395f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
405f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    private final class ImsServiceFeatureQuery implements ServiceConnection {
415f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
425f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        private static final String LOG_TAG = "ImsServiceFeatureQuery";
435f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
445f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        private final ComponentName mName;
455f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        private final String mIntentFilter;
465f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
475f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        ImsServiceFeatureQuery(ComponentName name, String intentFilter) {
485f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            mName = name;
495f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            mIntentFilter = intentFilter;
505f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        }
515f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
525f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        /**
535f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger         * Starts the bind to the ImsService specified ComponentName.
545f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger         * @return true if binding started, false if it failed and will not recover.
555f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger         */
565f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        public boolean start() {
575f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            Log.d(LOG_TAG, "start: intent filter=" + mIntentFilter + ", name=" + mName);
585f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            Intent imsServiceIntent = new Intent(mIntentFilter).setComponent(mName);
595f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
605f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                    | Context.BIND_IMPORTANT;
615f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            boolean bindStarted = mContext.bindService(imsServiceIntent, this, serviceFlags);
625f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            if (!bindStarted) {
635f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                // Docs say to unbind if this fails.
645f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                cleanup();
655f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            }
665f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            return bindStarted;
675f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        }
685f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
695f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        @Override
705f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        public void onServiceConnected(ComponentName name, IBinder service) {
715f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            Log.i(LOG_TAG, "onServiceConnected for component: " + name);
725f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            if (service != null) {
735f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                queryImsFeatures(IImsServiceController.Stub.asInterface(service));
745f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            } else {
755f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null, cleaning up.");
765f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                cleanup();
775f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            }
785f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        }
795f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
805f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        @Override
815f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        public void onServiceDisconnected(ComponentName name) {
825f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            Log.w(LOG_TAG, "onServiceDisconnected for component: " + name);
835f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        }
845f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
855f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        private void queryImsFeatures(IImsServiceController controller) {
865f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            ImsFeatureConfiguration config;
875f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            try {
885f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                config = controller.querySupportedImsFeatures();
895f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            } catch (RemoteException e) {
905f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                Log.w(LOG_TAG, "queryImsFeatures - error: " + e);
915f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                cleanup();
925f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                mListener.onError(mName);
935f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                return;
945f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            }
955f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            Set<ImsFeatureConfiguration.FeatureSlotPair> servicePairs = config.getServiceFeatures();
965f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            // Complete, remove from active queries and notify.
975f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            cleanup();
985f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            mListener.onComplete(mName, servicePairs);
995f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        }
1005f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
1015f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        private void cleanup() {
1025f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            mContext.unbindService(this);
1035f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            synchronized (mLock) {
1045f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                mActiveQueries.remove(mName);
1055f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            }
1065f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        }
1075f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    }
1085f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
1095f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    public interface Listener {
1105f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        /**
1115f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger         * Called when a query has completed.
1125f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger         * @param name The Package Name of the query
1135f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger         * @param features A Set of slotid->feature pairs that the ImsService supports.
1145f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger         */
1155f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        void onComplete(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features);
1165f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
1175f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        /**
1185f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger         * Called when a query has failed and should be retried.
1195f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger         */
1205f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        void onError(ComponentName name);
1215f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    }
1225f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
1235f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    // Maps an active ImsService query (by Package Name String) its query.
1245f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    private final Map<ComponentName, ImsServiceFeatureQuery> mActiveQueries = new HashMap<>();
1255f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    private final Context mContext;
1265f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    private final Listener mListener;
1275f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    private final Object mLock = new Object();
1285f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
1295f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    public ImsServiceFeatureQueryManager(Context context, Listener listener) {
1305f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        mContext = context;
1315f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        mListener = listener;
1325f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    }
1335f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
1345f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    /**
1355f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger     * Starts an ImsService feature query for the ComponentName and Intent specified.
1365f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger     * @param name The ComponentName of the ImsService being queried.
1375f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger     * @param intentFilter The Intent filter that the ImsService specified.
1385f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger     * @return true if the query started, false if it was unable to start.
1395f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger     */
1405f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    public boolean startQuery(ComponentName name, String intentFilter) {
1415f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        synchronized (mLock) {
1425f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            if (mActiveQueries.containsKey(name)) {
1435f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                // We already have an active query, wait for it to return.
1445f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger                return true;
1455f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            }
1465f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, intentFilter);
1475f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            mActiveQueries.put(name, query);
1485f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            return query.start();
1495f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        }
1505f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    }
1515f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger
1525f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    /**
1535f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger     * @return true if there are any active queries, false if the manager is idle.
1545f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger     */
1555f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    public boolean isQueryInProgress() {
1565f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        synchronized (mLock) {
1575f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger            return !mActiveQueries.isEmpty();
1585f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger        }
1595f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger    }
1605f6a521925b16e8c5f6dfdfa7dad928f65dfc362Brad Ebinger}
161