Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright 2017 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;
     18 
     19 import android.app.AlarmManager;
     20 import android.content.ContentResolver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.database.ContentObserver;
     24 import android.os.Handler;
     25 import android.os.SystemClock;
     26 import android.os.SystemProperties;
     27 import android.os.UserHandle;
     28 import android.provider.Settings;
     29 
     30 /**
     31  * An interface to various time / time zone detection behaviors that should be centralized into a
     32  * new service.
     33  */
     34 // Non-final to allow mocking.
     35 public class TimeServiceHelper {
     36 
     37     /**
     38      * Callback interface for automatic detection enable/disable changes.
     39      */
     40     public interface Listener {
     41         /**
     42          * Automatic time detection has been enabled or disabled.
     43          */
     44         void onTimeDetectionChange(boolean enabled);
     45 
     46         /**
     47          * Automatic time zone detection has been enabled or disabled.
     48          */
     49         void onTimeZoneDetectionChange(boolean enabled);
     50     }
     51 
     52     private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
     53 
     54     private final Context mContext;
     55     private final ContentResolver mCr;
     56 
     57     private Listener mListener;
     58 
     59     /** Creates a TimeServiceHelper */
     60     public TimeServiceHelper(Context context) {
     61         mContext = context;
     62         mCr = context.getContentResolver();
     63     }
     64 
     65     /**
     66      * Sets a listener that will be called when the automatic time / time zone detection setting
     67      * changes.
     68      */
     69     public void setListener(Listener listener) {
     70         if (listener == null) {
     71             throw new NullPointerException("listener==null");
     72         }
     73         if (mListener != null) {
     74             throw new IllegalStateException("listener already set");
     75         }
     76         this.mListener = listener;
     77         mCr.registerContentObserver(
     78                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
     79                 new ContentObserver(new Handler()) {
     80                     public void onChange(boolean selfChange) {
     81                         listener.onTimeDetectionChange(isTimeDetectionEnabled());
     82                     }
     83                 });
     84         mCr.registerContentObserver(
     85                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
     86                 new ContentObserver(new Handler()) {
     87                     public void onChange(boolean selfChange) {
     88                         listener.onTimeZoneDetectionChange(isTimeZoneDetectionEnabled());
     89                     }
     90                 });
     91     }
     92 
     93     /**
     94      * Returns the same value as {@link System#currentTimeMillis()}.
     95      */
     96     public long currentTimeMillis() {
     97         return System.currentTimeMillis();
     98     }
     99 
    100     /**
    101      * Returns the same value as {@link SystemClock#elapsedRealtime()}.
    102      */
    103     public long elapsedRealtime() {
    104         return SystemClock.elapsedRealtime();
    105     }
    106 
    107     /**
    108      * Returns true if the device has an explicit time zone set.
    109      */
    110     public boolean isTimeZoneSettingInitialized() {
    111         return isTimeZoneSettingInitializedStatic();
    112 
    113     }
    114 
    115     /**
    116      * Returns true if automatic time detection is enabled in settings.
    117      */
    118     public boolean isTimeDetectionEnabled() {
    119         try {
    120             return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME) > 0;
    121         } catch (Settings.SettingNotFoundException snfe) {
    122             return true;
    123         }
    124     }
    125 
    126     /**
    127      * Returns true if automatic time zone detection is enabled in settings.
    128      */
    129     public boolean isTimeZoneDetectionEnabled() {
    130         try {
    131             return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0;
    132         } catch (Settings.SettingNotFoundException snfe) {
    133             return true;
    134         }
    135     }
    136 
    137     /**
    138      * Set the device time zone and send out a sticky broadcast so the system can
    139      * determine if the timezone was set by the carrier.
    140      *
    141      * @param zoneId timezone set by carrier
    142      */
    143     public void setDeviceTimeZone(String zoneId) {
    144         setDeviceTimeZoneStatic(mContext, zoneId);
    145     }
    146 
    147     /**
    148      * Set the time and Send out a sticky broadcast so the system can determine
    149      * if the time was set by the carrier.
    150      *
    151      * @param time time set by network
    152      */
    153     public void setDeviceTime(long time) {
    154         SystemClock.setCurrentTimeMillis(time);
    155         Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
    156         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
    157         intent.putExtra("time", time);
    158         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    159     }
    160 
    161     /**
    162      * Static implementation of isTimeZoneSettingInitialized() for use from {@link MccTable}. This
    163      * is a hack to deflake TelephonyTests when running on a device with a real SIM: in that
    164      * situation real service events may come in while a TelephonyTest is running, leading to flakes
    165      * as the real / fake instance of TimeServiceHelper is swapped in and out from
    166      * {@link TelephonyComponentFactory}.
    167      */
    168     static boolean isTimeZoneSettingInitializedStatic() {
    169         // timezone.equals("GMT") will be true and only true if the timezone was
    170         // set to a default value by the system server (when starting, system server
    171         // sets the persist.sys.timezone to "GMT" if it's not set). "GMT" is not used by
    172         // any code that sets it explicitly (in case where something sets GMT explicitly,
    173         // "Etc/GMT" Olsen ID would be used).
    174         // TODO(b/64056758): Remove "timezone.equals("GMT")" hack when there's a
    175         // better way of telling if the value has been defaulted.
    176 
    177         String timeZoneId = SystemProperties.get(TIMEZONE_PROPERTY);
    178         return timeZoneId != null && timeZoneId.length() > 0 && !timeZoneId.equals("GMT");
    179     }
    180 
    181     /**
    182      * Static method for use by MccTable. See {@link #isTimeZoneSettingInitializedStatic()} for
    183      * explanation.
    184      */
    185     static void setDeviceTimeZoneStatic(Context context, String zoneId) {
    186         AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    187         alarmManager.setTimeZone(zoneId);
    188         Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
    189         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
    190         intent.putExtra("time-zone", zoneId);
    191         context.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    192     }
    193 }
    194