Start collecting mobile radio activity from the radio.

Hook in to the new radio API to find out when the radio
is active and use that to track its state in batter stats.
We also still have the data being tracked from the kernel's
emulation, and continue to use that if we don't get data from
the radio.

Currently this monitoring is turned off until some issues
in the radio can be fixed that are providing bad data.

Also add a new API to get estimated drain and charge times.

Change-Id: Ifc4900fabb8f848f9cda361dce698664ea75f175
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index 62eb663..0d1e122 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -20,6 +20,7 @@
 import android.net.LocalSocketAddress;
 import android.os.Build;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.SystemClock;
@@ -59,6 +60,8 @@
 
     private final PowerManager.WakeLock mWakeLock;
 
+    private final Looper mLooper;
+
     private INativeDaemonConnectorCallbacks mCallbacks;
     private Handler mCallbackHandler;
 
@@ -74,6 +77,13 @@
 
     NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
             int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl) {
+        this(callbacks, socket, responseQueueSize, logTag, maxLogSize, wl,
+                FgThread.get().getLooper());
+    }
+
+    NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
+            int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl,
+            Looper looper) {
         mCallbacks = callbacks;
         mSocket = socket;
         mResponseQueue = new ResponseQueue(responseQueueSize);
@@ -81,6 +91,7 @@
         if (mWakeLock != null) {
             mWakeLock.setReferenceCounted(true);
         }
+        mLooper = looper;
         mSequenceNumber = new AtomicInteger(0);
         TAG = logTag != null ? logTag : "NativeDaemonConnector";
         mLocalLog = new LocalLog(maxLogSize);
@@ -88,7 +99,7 @@
 
     @Override
     public void run() {
-        mCallbackHandler = new Handler(FgThread.get().getLooper(), this);
+        mCallbackHandler = new Handler(mLooper, this);
 
         while (true) {
             try {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 705862a..7ce45f7 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -58,6 +58,8 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.PhoneStateListener;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -143,21 +145,25 @@
         public static final int InterfaceDnsServerInfo    = 615;
     }
 
+    static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
+
     /**
      * Binder context for this service
      */
-    private Context mContext;
+    private final Context mContext;
 
     /**
      * connector object for communicating with netd
      */
-    private NativeDaemonConnector mConnector;
+    private final NativeDaemonConnector mConnector;
 
     private final Handler mFgHandler;
+    private final Handler mDaemonHandler;
+    private final PhoneStateListener mPhoneStateListener;
 
     private IBatteryStats mBatteryStats;
 
-    private Thread mThread;
+    private final Thread mThread;
     private CountDownLatch mConnectedSignal = new CountDownLatch(1);
 
     private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
@@ -191,6 +197,9 @@
     private volatile boolean mBandwidthControlEnabled;
     private volatile boolean mFirewallEnabled;
 
+    private boolean mMobileActivityFromRadio = false;
+    private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+
     private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
             new RemoteCallbackList<INetworkActivityListener>();
     private boolean mNetworkActive;
@@ -207,20 +216,35 @@
         mFgHandler = new Handler(FgThread.get().getLooper());
 
         if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
+            mConnector = null;
+            mThread = null;
+            mDaemonHandler = null;
+            mPhoneStateListener = null;
             return;
         }
 
-        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         // Don't need this wake lock, since we now have a time stamp for when
         // the network actually went inactive.  (It might be nice to still do this,
         // but I don't want to do it through the power manager because that pollutes the
         // battery stats history with pointless noise.)
+        //PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         PowerManager.WakeLock wl = null; //pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NETD_TAG);
 
         mConnector = new NativeDaemonConnector(
-                new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl);
+                new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl,
+                FgThread.get().getLooper());
         mThread = new Thread(mConnector, NETD_TAG);
 
+        mDaemonHandler = new Handler(FgThread.get().getLooper());
+        mPhoneStateListener = new PhoneStateListener(mDaemonHandler.getLooper()) {
+            public void onDataConnectionRealTimeInfoChanged(
+                    DataConnectionRealTimeInfo dcRtInfo) {
+                // Disabled for now, until we are getting good data.
+                //notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE,
+                //        dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true);
+            }
+        };
+
         // Add ourself to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
     }
@@ -368,36 +392,62 @@
     /**
      * Notify our observers of a change in the data activity state of the interface
      */
-    private void notifyInterfaceClassActivity(int type, boolean active, long tsNanos) {
-        try {
-            getBatteryStats().noteDataConnectionActive(type, active, tsNanos);
-        } catch (RemoteException e) {
-        }
-
-        final int length = mObservers.beginBroadcast();
-        try {
-            for (int i = 0; i < length; i++) {
+    private void notifyInterfaceClassActivity(int type, int powerState, long tsNanos,
+            boolean fromRadio) {
+        final boolean isMobile = ConnectivityManager.isNetworkTypeMobile(type);
+        if (isMobile) {
+            if (!fromRadio) {
+                if (mMobileActivityFromRadio) {
+                    // If this call is not coming from a report from the radio itself, but we
+                    // have previously received reports from the radio, then we will take the
+                    // power state to just be whatever the radio last reported.
+                    powerState = mLastPowerStateFromRadio;
+                }
+            } else {
+                mMobileActivityFromRadio = true;
+            }
+            if (mLastPowerStateFromRadio != powerState) {
+                mLastPowerStateFromRadio = powerState;
                 try {
-                    mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
-                            Integer.toString(type), active, tsNanos);
+                    getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos);
                 } catch (RemoteException e) {
-                } catch (RuntimeException e) {
                 }
             }
-        } finally {
-            mObservers.finishBroadcast();
+        }
+
+        boolean isActive = powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
+                || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
+
+        if (!isMobile || fromRadio || !mMobileActivityFromRadio) {
+            // Report the change in data activity.  We don't do this if this is a change
+            // on the mobile network, that is not coming from the radio itself, and we
+            // have previously seen change reports from the radio.  In that case only
+            // the radio is the authority for the current state.
+            final int length = mObservers.beginBroadcast();
+            try {
+                for (int i = 0; i < length; i++) {
+                    try {
+                        mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
+                                Integer.toString(type), isActive, tsNanos);
+                    } catch (RemoteException e) {
+                    } catch (RuntimeException e) {
+                    }
+                }
+            } finally {
+                mObservers.finishBroadcast();
+            }
         }
 
         boolean report = false;
         synchronized (mIdleTimerLock) {
             if (mActiveIdleTimers.isEmpty()) {
-                // If there are no idle times, we are not monitoring activity, so we
+                // If there are no idle timers, we are not monitoring activity, so we
                 // are always considered active.
-                active = true;
+                isActive = true;
             }
-            if (mNetworkActive != active) {
-                mNetworkActive = active;
-                report = active;
+            if (mNetworkActive != isActive) {
+                mNetworkActive = isActive;
+                report = isActive;
             }
         }
         if (report) {
@@ -611,10 +661,13 @@
                         try {
                             timestampNanos = Long.parseLong(cooked[4]);
                         } catch(NumberFormatException ne) {}
+                    } else {
+                        timestampNanos = SystemClock.elapsedRealtimeNanos();
                     }
                     boolean isActive = cooked[2].equals("active");
                     notifyInterfaceClassActivity(Integer.parseInt(cooked[3]),
-                            isActive, timestampNanos);
+                            isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+                            : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW, timestampNanos, false);
                     return true;
                     // break;
             case NetdResponseCode.InterfaceAddressChange:
@@ -1301,9 +1354,11 @@
             if (ConnectivityManager.isNetworkTypeMobile(type)) {
                 mNetworkActive = false;
             }
-            mFgHandler.post(new Runnable() {
+            mDaemonHandler.post(new Runnable() {
                 @Override public void run() {
-                    notifyInterfaceClassActivity(type, true, SystemClock.elapsedRealtimeNanos());
+                    notifyInterfaceClassActivity(type,
+                            DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+                            SystemClock.elapsedRealtimeNanos(), false);
                 }
             });
         }
@@ -1328,10 +1383,11 @@
                 throw e.rethrowAsParcelableException();
             }
             mActiveIdleTimers.remove(iface);
-            mFgHandler.post(new Runnable() {
+            mDaemonHandler.post(new Runnable() {
                 @Override public void run() {
-                    notifyInterfaceClassActivity(params.type, false,
-                            SystemClock.elapsedRealtimeNanos());
+                    notifyInterfaceClassActivity(params.type,
+                            DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+                            SystemClock.elapsedRealtimeNanos(), false);
                 }
             });
         }
@@ -1941,6 +1997,9 @@
         pw.println();
 
         pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
+        pw.print("mMobileActivityFromRadio="); pw.print(mMobileActivityFromRadio);
+                pw.print(" mLastPowerStateFromRadio="); pw.println(mLastPowerStateFromRadio);
+        pw.print("mNetworkActive="); pw.println(mNetworkActive);
 
         synchronized (mQuotaLock) {
             pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 037a744..d4565b6 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -393,6 +393,14 @@
         if (!checkNotifyPermission("notifyServiceState()")){
             return;
         }
+        long ident = Binder.clearCallingIdentity();
+        try {
+            mBatteryStats.notePhoneState(state.getState());
+        } catch (RemoteException re) {
+            // Can't do much
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
         synchronized (mRecords) {
             mServiceState = state;
             for (Record r : mRecords) {
@@ -802,15 +810,6 @@
     //
 
     private void broadcastServiceStateChanged(ServiceState state) {
-        long ident = Binder.clearCallingIdentity();
-        try {
-            mBatteryStats.notePhoneState(state.getState());
-        } catch (RemoteException re) {
-            // Can't do much
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-
         Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
         Bundle data = new Bundle();
         state.fillInNotifierBundle(data);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0ddb827..dbe773c 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -32,6 +32,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
@@ -248,10 +249,10 @@
         }
     }
 
-    public void noteDataConnectionActive(int type, boolean active, long timestampNs) {
+    public void noteMobileRadioPowerState(int powerState, long timestampNs) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteDataConnectionActive(type, active, timestampNs);
+            mStats.noteMobileRadioPowerState(powerState, timestampNs);
         }
     }