Book Excerpt: ACTION_PROCESS_TEXT Security
The following sections are excerpts from Version 6.9 of
“The Busy Coder’s Guide to Android Development”, with
slight modifications to fit the blog format. They are from
the same chapter that
introduced ACTION_PROCESS_TEXT
itself.
Limitations of ACTION_PROCESS_TEXT
ACTION_PROCESS_TEXT
is “not all unicorns and rainbows”. There
are a few issues that you will need to take into account.
Security
There is no documented android:permission
attribute to place on the
<activity>
that is offering ACTION_PROCESS_TEXT
, to limit callers.
Ideally, we could limit invocations of ACTION_PROCESS_TEXT
only to
the firmware itself. As it stands, any app can call startActivity()
(or, worse, startActivityForResult()
) for your ACTION_PROCESS_TEXT
activity and have your code process the text (with user intervention).
Please be sure that if you return data via EXTRA_PROCESS_TEXT
that the
data not include any private information or anything that needs to be
secured.
With luck, this will be improved in a future version of Android.
Landscape
In Android 6.0, the text-selection floating action mode for an EditText
does not appear in landscape mode, when the entire UI is dedicated to
the text-entry screen with the input method editor. With luck,
this too will be fixed in a future version of Android.
Blocking ACTION_PROCESS_TEXT
There will be cases where you do not want ACTION_PROCESS_TEXT
to be
offered to your users. For example, perhaps the text contains sensitive
information that should not be passed outside of your app.
The best solution, particularly for a TextView
, is to mark the text
as not being selectable. This is accomplished via
android:textIsSelectable="false"
in a layout file, or via
setTextIsSelectable(false)
in Java. false
is the default value
for TextView
.
However, for an EditText
widget, true
is the default is-selectable
state, and you cannot seem to override that with setTextIsSelectable(false)
.
There is no officially supported option for handling this case, though perhaps there will be one in the future.
One unsupported hack of a workaround relies upon the fact that
EditText
blocks the floating action mode for password fields. In the
source code to EditText
, TextView
, and related classes, this is
handled by seeing if the TransformationMethod
associated with the
widget is PasswordTransformationMethod
. A TransformationMethod
is responsible for on-the-fly adjustments between what the user types
and what the user sees, such as PasswordTransformationMethod
replacing
typed-in characters with dots.
Making your EditText
widget use PasswordTransformationMethod
itself
is fine for actual password fields. But suppose you have an EditText
whose contents should be kept private but should not have the
input-shrouding effect of PasswordTransformationMethod
. To offer this,
you would need to create a subclass of PasswordTransformationMethod
(so the block-the-floating-action-mode logic works) that does not actually
transform the text (to block the changes that PasswordTransformationMethod
would ordinarily apply).
A proof-of-concept implementation of this can be found in the
Introspection/ProcessTextBlocker
sample application. This is a clone of the FilesEditor
sample app from the book’s chapter on file I/O, where there are a series
of tabs in a ViewPager
, with a large EditText
widget in each tab,
managed by an EditorFragment
. This version of the sample implements one change: the
use of DummyTransformationMethod
:
private static class DummyTransformationMethod
extends PasswordTransformationMethod {
@Override
public CharSequence getTransformation(CharSequence source,
View view) {
return(source);
}
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
// no-op
}
@Override
public void onFocusChanged(View view,
CharSequence sourceText,
boolean focused, int direction,
Rect previouslyFocusedRect) {
// no-op
}
@Override
public void afterTextChanged(Editable s) {
// no-op
}
@Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
// no-op
}
}
This is a do-nothing TransformationMethod
. Ordinarily, this would be
completely useless. However, it inherits from PasswordTransformationMethod
,
which is what we need to block the floating action mode.
In onCreateView()
of the EditorFragment
, we apply a
DummyTransformationMethod
via setTransformationMethod()
:
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View result=inflater.inflate(R.layout.editor, container, false);
editor=(EditText)result.findViewById(R.id.editor);
editor.setTransformationMethod(new DummyTransformationMethod());
return(result);
}
However, this approach has limitations:
-
It only works in portrait, not landscape, for unclear reasons. Since
ACTION_PROCESS_TEXT
also only works in portrait, not landscape, we still succeed in blockingACTION_PROCESS_TEXT
options. -
It blocks the entire floating action mode (in portrait), clobbering the existing cut/copy/paste/select-all options that might ordinarily be there.
-
Since it is tied to internal implementation (that the floating action mode is suppressed when using an
instanceof
aPasswordTransformationMethod
), not only is this subject to change across Android versions, but also it is subject to change based on device manufacturer or custom ROM tweaks to the Android source code.