package com.mopub.common;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.view.Display;
import android.view.WindowManager;
import com.mopub.common.logging.MoPubLog;
import com.mopub.common.util.Utils;
import java.util.Locale;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
import static android.content.pm.PackageManager.NameNotFoundException;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
/**
* Singleton that caches Client objects so they will be available to background threads.
*/
public class ClientMetadata {
// Network type constant defined after API 9:
private static final int TYPE_ETHERNET = 9;
private static final String DEVICE_ORIENTATION_PORTRAIT = "p";
private static final String DEVICE_ORIENTATION_LANDSCAPE = "l";
private static final String DEVICE_ORIENTATION_SQUARE = "s";
private static final String DEVICE_ORIENTATION_UNKNOWN = "u";
private static final String IFA_PREFIX = "ifa:";
private static final String SHA_PREFIX = "sha:";
private static final int UNKNOWN_NETWORK = -1;
private String mNetworkOperatorForUrl;
private final String mNetworkOperator;
private String mSimOperator;
private final String mIsoCountryCode;
private final String mSimIsoCountryCode;
private String mNetworkOperatorName;
private String mSimOperatorName;
private String mUdid;
private boolean mDoNotTrack = false;
private boolean mAdvertisingInfoSet = false;
public static enum MoPubNetworkType {
UNKNOWN(0),
ETHERNET(1),
WIFI(2),
MOBILE(3);
private final int mId;
private MoPubNetworkType(int id) {
mId = id;
}
@Override
public String toString() {
return Integer.toString(mId);
}
private static MoPubNetworkType fromAndroidNetworkType(int type) {
switch(type) {
case TYPE_ETHERNET:
return ETHERNET;
case ConnectivityManager.TYPE_WIFI:
return WIFI;
case ConnectivityManager.TYPE_MOBILE:
case ConnectivityManager.TYPE_MOBILE_DUN:
case ConnectivityManager.TYPE_MOBILE_HIPRI:
case ConnectivityManager.TYPE_MOBILE_MMS:
case ConnectivityManager.TYPE_MOBILE_SUPL:
return MOBILE;
default:
return UNKNOWN;
}
}
}
private static ClientMetadata sInstance;
// Cached client metadata used for generating URLs and events.
private final String mDeviceManufacturer;
private final String mDeviceModel;
private final String mDeviceProduct;
private final String mDeviceOsVersion;
private final int mDeviceScreenWidthPx;
private final int mDeviceScreenHeightPx;
private final String mSdkVersion;
private final String mAppVersion;
private final String mAppPackageName;
private String mAppName;
private final Context mContext;
private final ConnectivityManager mConnectivityManager;
/**
* Returns the singleton ClientMetadata object, using the context to obtain data if necessary.
*/
public static ClientMetadata getInstance(Context context) {
// Use a local variable so we can reduce accesses of the volatile field.
ClientMetadata result = sInstance;
if (result == null) {
synchronized (ClientMetadata.class) {
result = sInstance;
if (result == null) {
result = new ClientMetadata(context);
sInstance = result;
}
}
}
return result;
}
/**
* Can be used by background threads and other objects without a context to attempt to get
* ClientMetadata. If the object has never been referenced from a thread with a context,
* this will return null.
*/
public static ClientMetadata getInstance() {
ClientMetadata result = sInstance;
if (result == null) {
// If it's being initialized in another thread, wait for the lock.
synchronized (ClientMetadata.class) {
result = sInstance;
}
}
return result;
}
// NEVER CALL THIS AS A USER. Get it from the Singletons class.
public ClientMetadata(Context context) {
mContext = context.getApplicationContext();
mConnectivityManager =
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
mDeviceManufacturer = Build.MANUFACTURER;
mDeviceModel = Build.MODEL;
mDeviceProduct = Build.PRODUCT;
mDeviceOsVersion = Build.VERSION.RELEASE;
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
mDeviceScreenWidthPx = display.getWidth();
mDeviceScreenHeightPx = display.getHeight();
mSdkVersion = MoPub.SDK_VERSION;
// Cache context items that don't change:
mAppVersion = getAppVersionFromContext(mContext);
PackageManager packageManager = mContext.getPackageManager();
ApplicationInfo applicationInfo = null;
mAppPackageName = context.getPackageName();
try {
applicationInfo = packageManager.getApplicationInfo(mAppPackageName, 0);
} catch (final NameNotFoundException e) {
// swallow
}
if (applicationInfo != null) {
mAppName = (String) packageManager.getApplicationLabel(applicationInfo);
}
final TelephonyManager telephonyManager =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mNetworkOperatorForUrl = telephonyManager.getNetworkOperator();
mNetworkOperator = telephonyManager.getNetworkOperator();
if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA &&
telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY) {
mNetworkOperatorForUrl = telephonyManager.getSimOperator();
mSimOperator = telephonyManager.getSimOperator();
}
mIsoCountryCode = telephonyManager.getNetworkCountryIso();
mSimIsoCountryCode = telephonyManager.getSimCountryIso();
try {
// Some Lenovo devices require READ_PHONE_STATE here.
mNetworkOperatorName = telephonyManager.getNetworkOperatorName();
if (telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY) {
mSimOperatorName = telephonyManager.getSimOperatorName();
}
} catch (SecurityException e) {
mNetworkOperatorName = null;
mSimOperatorName = null;
}
// Get the device ID. This will be replaced later when the Play Services callbacks complete.
mUdid = getDeviceIdFromContext(mContext);
}
private static String getAppVersionFromContext(Context context) {
try {
final String packageName = context.getPackageName();
final PackageInfo packageInfo =
context.getPackageManager().getPackageInfo(packageName, 0);
return packageInfo.versionName;
} catch (Exception exception) {
MoPubLog.d("Failed to retrieve PackageInfo#versionName.");
return null;
}
}
private static String getDeviceIdFromContext(Context context) {
String deviceId = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ANDROID_ID);
deviceId = (deviceId == null) ? "" : Utils.sha1(deviceId);
return SHA_PREFIX + deviceId;
}
/**
* @return the display orientation. Useful when generating ad requests.
*/
public String getOrientationString() {
final int orientationInt = mContext.getResources().getConfiguration().orientation;
String orientation = DEVICE_ORIENTATION_UNKNOWN;
if (orientationInt == Configuration.ORIENTATION_PORTRAIT) {
orientation = DEVICE_ORIENTATION_PORTRAIT;
} else if (orientationInt == Configuration.ORIENTATION_LANDSCAPE) {
orientation = DEVICE_ORIENTATION_LANDSCAPE;
} else if (orientationInt == Configuration.ORIENTATION_SQUARE) {
orientation = DEVICE_ORIENTATION_SQUARE;
}
return orientation;
}
public MoPubNetworkType getActiveNetworkType() {
int networkType = UNKNOWN_NETWORK;
if (mContext.checkCallingOrSelfPermission(ACCESS_NETWORK_STATE) == PERMISSION_GRANTED) {
NetworkInfo activeNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
networkType = activeNetworkInfo != null
? activeNetworkInfo.getType() : UNKNOWN_NETWORK;
}
return MoPubNetworkType.fromAndroidNetworkType(networkType);
}
/**
* Get the logical density of the display as in {@link android.util.DisplayMetrics#density}
*/
public float getDensity() {
return mContext.getResources().getDisplayMetrics().density;
}
/**
* @return the network operator for URL generators.
*/
public String getNetworkOperatorForUrl() {
return mNetworkOperatorForUrl;
}
/**
* @return the network operator.
*/
public String getNetworkOperator() {
return mNetworkOperator;
}
public Locale getDeviceLocale() {
return mContext.getResources().getConfiguration().locale;
}
/**
* @return the sim operator.
*/
public String getSimOperator() {
return mSimOperator;
}
/**
* @return the country code of the device.
*/
public String getIsoCountryCode() {
return mIsoCountryCode;
}
/**
* @return the sim provider's country code.
*/
public String getSimIsoCountryCode() {
return mSimIsoCountryCode;
}
/**
* @return the network operator name.
*/
public String getNetworkOperatorName() {
return mNetworkOperatorName;
}
/**
* @return the sim operator name.
*/
public String getSimOperatorName() {
return mSimOperatorName;
}
/**
* @return the stored device ID.
*/
public synchronized String getDeviceId() {
return mUdid;
}
/**
* @return the user's do not track preference. Should be set whenever a getAdInfo() call is
* completed.
*/
public synchronized boolean isDoNotTrackSet() {
return mDoNotTrack;
}
public synchronized void setAdvertisingInfo(String advertisingId, boolean doNotTrack) {
mUdid = IFA_PREFIX + advertisingId;
mDoNotTrack = doNotTrack;
mAdvertisingInfoSet = true;
}
public synchronized boolean isAdvertisingInfoSet() {
return mAdvertisingInfoSet;
}
/**
* @return the device manufacturer.
*/
public String getDeviceManufacturer() {
return mDeviceManufacturer;
}
/**
* @return the device model identifier.
*/
public String getDeviceModel() {
return mDeviceModel;
}
/**
* @return the device product identifier.
*/
public String getDeviceProduct() {
return mDeviceProduct;
}
/**
* @return the device os version.
*/
public String getDeviceOsVersion() {
return mDeviceOsVersion;
}
/**
* @return the device screen width in pixels.
*/
public int getDeviceScreenWidthPx() {
return mDeviceScreenWidthPx;
}
/**
* @return the device screen height in pixels.
*/
public int getDeviceScreenHeightPx() {
return mDeviceScreenHeightPx;
}
/**
* @return the MoPub SDK Version.
*/
public String getSdkVersion() {
return mSdkVersion;
}
/**
* @return the version of the application the SDK is included in.
*/
public String getAppVersion() {
return mAppVersion;
}
/**
* @return the package of the application the SDK is included in.
*/
public String getAppPackageName() {
return mAppPackageName;
}
/**
* @return the name of the application the SDK is included in.
*/
public String getAppName() {
return mAppName;
}
@VisibleForTesting
public static void clearForTesting() {
sInstance = null;
}
}