package eu.musesproject.client.contextmonitoring.sensors; /* * #%L * musesclient * %% * Copyright (C) 2013 - 2014 HITEC * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.ActivityManager.RunningServiceInfo; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.AsyncTask; import android.os.Build; import android.util.Log; import eu.musesproject.client.R; import eu.musesproject.client.contextmonitoring.ContextListener; import eu.musesproject.client.db.entity.SensorConfiguration; import eu.musesproject.contextmodel.ContextEvent; import java.util.ArrayList; import java.util.List; /** * @author christophstanik * * Class to collect information about the currently used app by the user. * The class collects information about: * - foreground app name * - background processes name */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) public class AppSensor implements ISensor { private static final String TAG = AppSensor.class.getSimpleName(); // sensor identifier public static final String TYPE = "CONTEXT_SENSOR_APP"; // time in milliseconds when the sensor polls information private static int OBSERVATION_INTERVALL = 1000; // maximal number of how many background services are stored if a context event is fired private static final int MAX_SHOWN_BACKGROUND_SERVICES = 1; // context property keys public static final String PROPERTY_KEY_ID = "id"; public static final String PROPERTY_KEY_APP_NAME = "appname"; public static final String PROPERTY_KEY_PACKAGE_NAME = "packagename"; public static final String PROPERTY_KEY_APP_VERSION = "appversion"; public static final String PROPERTY_KEY_BACKGROUND_PROCESS = "backgroundprocess"; private Context context; private ContextListener listener; // stores all fired context events of this sensor private List<ContextEvent> contextEventHistory; // get current application information private ActivityManager activityManager; // holds a value that indicates if the sensor is enabled or disabled private boolean sensorEnabled; public AppSensor(Context context) { this.context = context; init(); } // initializes all necessary default values private void init() { sensorEnabled = false; activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); contextEventHistory = new ArrayList<ContextEvent>(CONTEXT_EVENT_HISTORY_SIZE); } /** * creates the context event for this sensor and saves it in the * context event history * @param runningServices list of background services * @param appName name of the currently active application */ private void createContextEvent(String appName, String packageName, int appVersion, List<RunningServiceInfo> runningServices) { // get the running services List<String> runningServicesNames = new ArrayList<String>(); for (RunningServiceInfo runningServiceInfo : runningServices) { runningServicesNames.add(runningServiceInfo.process); } // create the context event ContextEvent contextEvent = new ContextEvent(); contextEvent.setType(TYPE); contextEvent.setTimestamp(System.currentTimeMillis()); contextEvent.addProperty(PROPERTY_KEY_ID, String.valueOf(contextEventHistory != null ? (contextEventHistory.size() + 1) : -1)); contextEvent.addProperty(PROPERTY_KEY_APP_NAME, appName); contextEvent.addProperty(PROPERTY_KEY_PACKAGE_NAME, packageName); contextEvent.addProperty(PROPERTY_KEY_APP_VERSION, String.valueOf(appVersion)); contextEvent.addProperty(PROPERTY_KEY_BACKGROUND_PROCESS, runningServicesNames.toString()); contextEvent.generateId(); // add context event to the context event history contextEventHistory.add(contextEvent); if(contextEventHistory.size() > CONTEXT_EVENT_HISTORY_SIZE) { contextEventHistory.remove(0); } if(listener != null) { Log.d(TAG, "called: listener.onEvent(contextEvent); app name: " + appName); listener.onEvent(contextEvent); } } @Override public void enable() { //Log.d(TAG, "app sensor enable"); if (!sensorEnabled) { //Log.d(TAG, "start app tracking"); sensorEnabled = true; new AppObserver().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } @Override public void disable() { if(sensorEnabled) { //Log.d(TAG, "stop app tracking"); sensorEnabled = false; } } /** * Observes the users application usage. Creates a context event whenever a new application is started. */ private class AppObserver extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { String previousApp = "MUSES"; while (sensorEnabled) { // get the first item in the list, because it is the foreground task RunningTaskInfo foregroundTaskInfo = activityManager.getRunningTasks(1).get(0); String foregroundTaskPackageName = foregroundTaskInfo.topActivity.getPackageName(); PackageManager pm = context.getPackageManager(); PackageInfo foregroundAppPackageInfo; String foregroundTaskAppName = ""; int appVersion; List<RunningServiceInfo> runningServices; try { foregroundAppPackageInfo = pm.getPackageInfo(foregroundTaskPackageName, 0); foregroundTaskAppName = foregroundAppPackageInfo.applicationInfo.loadLabel(pm).toString(); appVersion = foregroundAppPackageInfo.versionCode; runningServices = activityManager.getRunningServices(MAX_SHOWN_BACKGROUND_SERVICES); // lollipop removed the other way to retrieve the app name if(android.os.Build.VERSION.SDK_INT >= 21 || foregroundTaskAppName.isEmpty()) { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> tasks = manager.getRunningAppProcesses(); foregroundTaskPackageName = tasks.get(0).processName; foregroundTaskAppName = getAppName(foregroundTaskPackageName); } // if the foreground application changed, create a context event if(!foregroundTaskAppName.equals(previousApp)) { Log.d(TAG, "foreground app name=" + foregroundTaskAppName + " | previous app name" + previousApp + " | is same="+(foregroundTaskAppName.equals(previousApp))); if(!foregroundTaskAppName.equals(context.getResources().getString(R.string.app_name))) { createContextEvent(foregroundTaskAppName, foregroundTaskPackageName, appVersion, runningServices); previousApp = foregroundTaskAppName; } } Thread.sleep(OBSERVATION_INTERVALL); } catch (NameNotFoundException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } private String getAppName(String pckName) { String appName = ""; PackageManager pm = context.getPackageManager(); try { PackageInfo pckInfo = pm.getPackageInfo(pckName, 0); appName = pckInfo.applicationInfo.loadLabel(pm).toString(); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return appName; } } @Override public void addContextListener(ContextListener listener) { this.listener = listener; } @Override public void removeContextListener(ContextListener listener) { this.listener = listener; } @Override public ContextEvent getLastFiredContextEvent() { if(contextEventHistory.size() > 0) { return contextEventHistory.get(contextEventHistory.size() - 1); } else { return null; } } @Override public void configure(List<SensorConfiguration> config) { // this sensor does not need any configuration } @Override public String getSensorType() { return TYPE; } }