1 /*
2  * Copyright (C) 2024 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.car.datasubscription;
18 
19 
20 import static com.android.car.datasubscription.DataSubscriptionStatus.PAID;
21 
22 import android.content.Context;
23 import android.database.ContentObserver;
24 import android.provider.Settings;
25 
26 import androidx.annotation.GuardedBy;
27 
28 /**
29  * This class provides a mechanism to indicate if a data plan is provided on a trial vs paid basis.
30  */
31 public class DataSubscription {
32     public static final String DATA_SUBSCRIPTION_ACTION =
33             "android.intent.action.DATA_SUBSCRIPTION";
34     private static final String SETTING = "car_data_subscription_status";
35     private static final int MIN_VALUE = 1;
36     private static final int MAX_VALUE = 3;
37 
38     @DataSubscriptionStatus private static final int DEFAULT_VALUE = PAID;
39 
40     @GuardedBy("this")
41     private DataSubscriptionChangeListener mDataSubscriptionChangeListener;
42     private final Context mContext;
43     private final ContentObserver mContentObserver =
44             new ContentObserver(/*  handler= */ null) {
45                 @Override
46                 public void onChange(boolean selfChange) {
47                     synchronized (DataSubscription.this) {
48                         if (mDataSubscriptionChangeListener != null) {
49                             mDataSubscriptionChangeListener.onChange(getDataSubscriptionStatus());
50                         }
51                     }
52                 }
53             };
54 
DataSubscription(Context context)55     public DataSubscription(Context context) {
56         mContext = context;
57 
58     }
59 
60     /** Returns the data subscription status of the vehicle. */
61     @DataSubscriptionStatus
getDataSubscriptionStatus()62     public int getDataSubscriptionStatus() {
63         int subscriptionStatus =
64                 Settings.Global.getInt(mContext.getContentResolver(), SETTING, DEFAULT_VALUE);
65         return (subscriptionStatus < MIN_VALUE || subscriptionStatus > MAX_VALUE)
66                 ? DEFAULT_VALUE
67                 : subscriptionStatus;
68     }
69 
70     /**
71      * Registers a listener to data subscription status change updates. Supports registering
72      * only one listener. Throws IllegalStateException if a listeners is already registered.
73      */
addDataSubscriptionListener(DataSubscriptionChangeListener listener)74     public synchronized void addDataSubscriptionListener(DataSubscriptionChangeListener listener) {
75         if (mDataSubscriptionChangeListener != null) {
76             throw new IllegalStateException(
77                     "A listener is already registered. Multiple listeners are not supported.");
78         }
79         mDataSubscriptionChangeListener = listener;
80         mContext
81                 .getContentResolver()
82                 .registerContentObserver(
83                         Settings.Global.getUriFor(SETTING),
84                         /* notifyForDescendants=  */ false,
85                         mContentObserver);
86     }
87 
88     /**
89      * Unregisters the listener for data subscription status change updates. This method must be
90      * called after registering a listener to avoid memory leaks. Throws IllegalStateException if no
91      * listener is registered.
92      */
removeDataSubscriptionListener()93     public synchronized void removeDataSubscriptionListener() {
94         if (mDataSubscriptionChangeListener == null) {
95             throw new IllegalStateException("No listener is registered.");
96         }
97         mContext.getContentResolver().unregisterContentObserver(mContentObserver);
98         mDataSubscriptionChangeListener = null;
99     }
100 
101     /**
102      * Checks if the data subscription status is inactive
103      */
isDataSubscriptionInactive()104     public boolean isDataSubscriptionInactive() {
105         return getDataSubscriptionStatus() == DataSubscriptionStatus.INACTIVE;
106     }
107 
108     /**
109      * Interface to implement for listening to Data Subscription status changes.
110      */
111     public interface DataSubscriptionChangeListener {
112         /**
113          * Receive the Data Subscription status changes.
114          */
onChange(@ataSubscriptionStatus int value)115         void onChange(@DataSubscriptionStatus int value);
116     }
117 }
118