Cyanogenmod - Privacy Guard's location access false positive

     Cyanogenmod privacy guard feature is so promising and lets users get better idea about application's permissions. This is something similar to the feature available in iOS and user get to decide if they want to allow/deny access at runtime. Ofcourse, this doesn't guarantee that the application would be functional at all times but its better to be safe than sorry. This is even more helpful for application developers using 3rd party libraries.

    However, i recently came across a warning in an application about location access without any such permission declaration in its manifest. This was seen in OnePlus One running CM 11. This is kind of creepy, especially when features like Privacy Guard isn't enabled by default and is available only in CM builds. Things like this could get application developers in legal trouble. Having said this, its still possible that a framework bug could lead to false positives and mislead users.


    Privacy guard is based on AOSP's AppOpsService. Any critical request from applications via SDK is routed through this service to warn users. So a quick verification is to brute force all such APIs from a sample application and check out the behavior. It turns out that using one of the WiFiManager APIs along with Privacy Guard does exhibit false positives.

        WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        wifi.getScanResults();

  This API requires that application need the following permission to be declared in their manifest.

   <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

   And yet, CM warns users about the application requesting access to location. How in the world does searching for access points translate into a location access request? Obviously, the application gets low rating and bad PR and sadly this is due to a framework bug where in a wrong string resource is associated with this request.

    public List<ScanResult> getScanResults(String callingPackage) {
        enforceAccessPermission();
        int userId = UserHandle.getCallingUserId();
        int uid = Binder.getCallingUid();
        long ident = Binder.clearCallingIdentity();
        try {
            if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
                    != AppOpsManager.MODE_ALLOWED) {
                return new ArrayList<ScanResult>();
            }
            int currentUser = ActivityManager.getCurrentUser();
            if (userId != currentUser) {
                return new ArrayList<ScanResult>();
            } else {
                return mWifiStateMachine.syncGetScanResultsList();
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }

    }

   WiFiService indeed checks for the desired operation, OP_WIFI_SCAN. However, the code corresponding to this operation (public static final int OP_WIFI_SCAN = 10) is associated with a string resource identifying a location access. The code serves as an index into a string array defined in a resource file.

    <string-array name="app_ops_labels" translatable="false">
        <item>@string/app_ops_access_location</item>
        <item>@string/app_ops_access_location</item>
        <item>@string/app_ops_access_location</item>
        <item>@string/app_ops_use_vibrate</item>
        <item>@string/app_ops_read_contacts</item>
        <item>@string/app_ops_modify_contacts</item>
        <item>@string/app_ops_read_call_log</item>
        <item>@string/app_ops_modify_call_log</item>
        <item>@string/app_ops_read_calendar</item>
        <item>@string/app_ops_modify_calendar</item>
        <item>@string/app_ops_access_location</item>
        <item>@string/app_ops_post_notification</item>
        <item>@string/app_ops_access_location</item>
        <item>@string/app_ops_make_phone_call</item>
        <item>@string/app_ops_read_sms</item>
        <item>@string/app_ops_write_sms</item>
             ...
             ...
    </string-array>

No comments: