// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.datausage;
import android.content.Context;
import android.text.TextUtils;
import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ContextUtils;
import org.chromium.base.PackageUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeClassQualifiedName;
import org.chromium.chrome.browser.ChromeApplication;
/**
* This class provides a base class implementation of a data use observer that is external to
* Chromium. This class should be accessed only on UI thread.
*/
@JNINamespace("chrome::android")
public class ExternalDataUseObserver {
/**
* Listens for application state changes and whenever Chromium state changes to running, checks
* and notifies {@link #ExternalDataUseObserverBridge} if the control app gets installed or
* uninstalled.
*/
private class ControlAppManager implements ApplicationStatus.ApplicationStateListener {
// Package name of the control app.
private final String mControlAppPackageName;
// True if the control app is installed.
private boolean mInstalled;
ControlAppManager(String controlAppPackageName) {
mControlAppPackageName = controlAppPackageName;
mInstalled = false;
ApplicationStatus.registerApplicationStateListener(this);
checkAndNotifyPackageInstallState();
if (!mInstalled) {
// Notify the state when the control app is not installed on startup.
nativeOnControlAppInstallStateChange(mNativeExternalDataUseObserverBridge, false);
}
}
@Override
public void onApplicationStateChange(int newState) {
if (newState == ApplicationState.HAS_RUNNING_ACTIVITIES) {
checkAndNotifyPackageInstallState();
}
}
/**
* Checks if the control app is installed or uninstalled and notifies {@link
* #ExternalDataUseObserverBridge} if there is change of installation state.
*/
private void checkAndNotifyPackageInstallState() {
// Check if native object is destroyed. This may happen at the time of Chromium
// shutdown.
if (mNativeExternalDataUseObserverBridge == 0) {
return;
}
if (TextUtils.isEmpty(mControlAppPackageName)) {
return;
}
boolean isControlAppInstalled =
PackageUtils.getPackageVersion(
ContextUtils.getApplicationContext(), mControlAppPackageName)
!= -1;
if (isControlAppInstalled != mInstalled) {
mInstalled = isControlAppInstalled;
nativeOnControlAppInstallStateChange(
mNativeExternalDataUseObserverBridge, mInstalled);
}
}
}
/**
* Pointer to the native ExternalDataUseObserverBridge object.
*/
private long mNativeExternalDataUseObserverBridge;
/**
* {@link #ControlAppManager} object that notifies when control app is installed.
*/
private ControlAppManager mControlAppManager;
@CalledByNative
private static ExternalDataUseObserver create(Context context, long nativePtr) {
return ((ChromeApplication) context).createExternalDataUseObserver(nativePtr);
}
/**
* Creates an instance of {@link #ExternalDataUseObserver}.
* @param nativePtr pointer to the native ExternalDataUseObserver object.
*/
public ExternalDataUseObserver(long nativePtr) {
mNativeExternalDataUseObserverBridge = nativePtr;
assert mNativeExternalDataUseObserverBridge != 0;
}
/**
* @return the default control app package name.
*/
protected String getDefaultControlAppPackageName() {
return "";
}
/**
* @return the google variation id.
* TODO(rajendrant): This function is unused, and should be removed.
*/
protected int getGoogleVariationID() {
return 0;
}
/**
* Initializes the control app manager with package name of the control app.
* @param controlAppPackageName package name of the control app. If this is empty the default
* control app package name from {@link getDefaultControlAppPackageName} will be used.
*/
@CalledByNative
protected void initControlAppManager(String controlAppPackageName) {
if (TextUtils.isEmpty(controlAppPackageName)) {
controlAppPackageName = getDefaultControlAppPackageName();
}
mControlAppManager = new ControlAppManager(controlAppPackageName);
}
/**
* Notification that the native object has been destroyed.
*/
@CalledByNative
private void onDestroy() {
mNativeExternalDataUseObserverBridge = 0;
}
/**
* Fetches matching rules and returns them via {@link #fetchMatchingRulesDone}. subsequent
* calls to this method While the fetch is underway, may cause the {@link
* #fetchMatchingRulesDone} callback to be missed for the subsequent call.
*/
@CalledByNative
protected void fetchMatchingRules() {
fetchMatchingRulesDone(null, null, null);
}
/*
* {@link #fetchMatchingRulesDone} reports the result of {@link #fetchMatchingRules} to
* the native.
* @param appPackageName package name of the app that should be matched.
* @domainPathRegex regex in RE2 syntax that is used for matching URLs.
* @param label opaque label that must be applied to the data use reports, and must uniquely
* identify the matching rule. Each element in {@link #label} must have non-zero length.
* The three vectors are should have equal length. All three vectors may be empty which
* implies that no matching rules are active.
*/
protected void fetchMatchingRulesDone(
String[] appPackageName, String[] domainPathRegEx, String[] label) {
// Check if native object is destroyed. This may happen at the time of Chromium shutdown.
if (mNativeExternalDataUseObserverBridge == 0) {
return;
}
nativeFetchMatchingRulesDone(
mNativeExternalDataUseObserverBridge, appPackageName, domainPathRegEx, label);
}
/**
* Asynchronously reports data use to the external observer.
* @param label the label provided by {@link #ExternalDataUseObserver} for the matching rule.
* @param tag “ChromeCustomTab” for Chrome custom tab, or "ChromeTab" for a default tab.
* @param networkType type of the network on which the traffic was exchanged. This integer value
* must map to NetworkChangeNotifier.ConnectionType.
* @param mccMnc MCCMNC of the network on which the traffic was exchanged.
* @param startTimeInMillis start time of the report in milliseconds since the Epoch (January
* 1st 1970, 00:00:00.000).
* @param endTimeInMillis end time of the report in milliseconds since the Epoch (January 1st
* 1970, 00:00:00.000).
* @param bytesDownloaded number of bytes downloaded by Chromium.
* @param bytesUploaded number of bytes uploaded by Chromium.
* The result of this request is returned asynchronously via
* {@link #nativeOnReportDataUseDone}. A new report should preferably be submitted only after
* the result of the previous report has been returned via {@link #nativeOnReportDataUseDone}.
* Submitting another data use report while the previous is pending may cause the previous
* report to be lost.
*/
@CalledByNative
protected void reportDataUse(String label, String tag, int networkType, String mccMnc,
long startTimeInMillis, long endTimeInMillis, long bytesDownloaded,
long bytesUploaded) {}
/*
* {@link #onReportDataUseDone} reports the result of {@link #reportDataUse} to
* the native.
* @param success true if the data report was successfully submitted to the external observer.
*/
protected void onReportDataUseDone(boolean success) {
// Check if native object is destroyed. This may happen at the time of Chromium shutdown.
if (mNativeExternalDataUseObserverBridge == 0) {
return;
}
nativeOnReportDataUseDone(mNativeExternalDataUseObserverBridge, success);
}
@NativeClassQualifiedName("ExternalDataUseObserverBridge")
private native void nativeFetchMatchingRulesDone(long nativeExternalDataUseObserver,
String[] appPackageName, String[] domainPathRegEx, String[] label);
@NativeClassQualifiedName("ExternalDataUseObserverBridge")
private native void nativeOnReportDataUseDone(
long nativeExternalDataUseObserver, boolean success);
@NativeClassQualifiedName("ExternalDataUseObserverBridge")
private native void nativeOnControlAppInstallStateChange(
long nativeExternalDataUseObserver, boolean isControlAppInstalled);
}